Skip to content

Instantly share code, notes, and snippets.

@essenwbb
Last active January 6, 2020 13:23
Show Gist options
  • Save essenwbb/38c1b38e49e2c1eba9b0d872225e7b9b to your computer and use it in GitHub Desktop.
Save essenwbb/38c1b38e49e2c1eba9b0d872225e7b9b to your computer and use it in GitHub Desktop.

go test

测试函数

  • 测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头

    满足以下思想:

    • go中大写字母开头的函数表示对外可见,即可理解为对外的API
    • TDD中的重要思想之一:Test behavior, not implementation.(less is more)
  • go test命令如果没有参数指定包那么将默认采用当前目录对应的包(和go build命令一样)

    • 参数-v可用于打印每个测试函数的名字和运行时间
    • 参数-run对应一个正则表达式,只有测试函数名被它正确匹配的测试函数才会被go test测试命令运行

    一旦我们已经修复了失败的测试用例,在我们提交代码更新之前,我们应该以不带参数的go test命令运行全部的测试用例,以确保修复失败测试的同时没有引入新的问题

    • 表格驱动的测试

      可以将所有测试数据合并到了一个测试中的表格中

    func TestIsPalindrome(t *testing.T) {
        var tests = []struct {
            input string
            want  bool
        }{
            {"", true},
            {"a", true},
            {"aa", true},
            {"ab", false},
            {"kayak", true},
            {"detartrated", true},
            {"A man, a plan, a canal: Panama", true},
            {"Evil I did dwell; lewd did I live.", true},
            {"Able was I ere I saw Elba", true},
            {"été", true},
            {"Et se resservir, ivresse reste.", true},
            {"palindrome", false}, // non-palindrome
            {"desserts", false},   // semi-palindrome
        }
        for _, test := range tests {
            if got := IsPalindrome(test.input); got != test.want {
                t.Errorf("IsPalindrome(%q) = %v", test.input, got)
            }
        }
    }

    即使表格中前面的数据导致了测试的失败,表格后面的测试数据依然会运行测试,因此在一个测试中我们可能了解多个失败的信息

    如果我们真的需要停止测试,或许是因为初始化失败或可能是早先的错误导致了后续错误等原因,我们可以使用t.Fatal或t.Fatalf停止当前测试函数。它们必须在和测试函数同一个goroutine内调用

    • 随机测试

      通过构造更广泛的随机输入来测试探索函数的行为

    • 测试main包

测试覆盖率

测试能证明缺陷存在,而无法证明没有缺陷 对待测程序执行的测试的程度称为测试的覆盖率。测试覆盖率并不能量化——即使最简单的程序的动态也是难以精确测量的 语句的覆盖率是指在测试中至少被运行一次的代码占总代码数的比例

go test -run=Coverage -coverprofile=c.out [code path]

如果使用了-covermode=count标志参数,那么将在每个代码块插入一个计数器而不是布尔标志量。在统计结果中记录了每个块的执行次数,这可以用于衡量哪些是被频繁执行的热点代码

为了收集数据,我们运行了测试覆盖率工具,打印了测试日志,生成一个HTML报告,然后在浏览器中打开(绿色为覆盖,红色为未覆盖)

go tool cover -html=c.out

实现100%的测试覆盖率听起来很美,但是在具体实践中通常是不可行的,也不是值得推荐的做法。因为那只能说明代码被执行过而已,并不意味着代码就是没有BUG的;因为对于逻辑复杂的语句需要针对不同的输入执行多次。有一些语句,例如上面的panic语句则永远都不会被执行到。另外,还有一些隐晦的错误在现实中很少遇到也很难编写对应的测试代码。测试从本质上来说是一个比较务实的工作,编写测试代码和编写应用代码的成本对比是需要考虑的。测试覆盖率工具可以帮助我们快速识别测试薄弱的地方,但是设计好的测试用例和编写应用代码一样需要严密的思考

基准测试

基准测试是测量一个程序在固定工作负载下的性能

在Go语言中,基准测试函数和普通测试函数写法类似,但是以Benchmark为前缀名,并且带有一个*testing.B类型的参数;testing.B参数除了提供和testing.T类似的方法,还有额外一些和性能测量相关的方法。它还提供了一个整数N,用于指定操作执行的循环次数。

import "testing"
func BenchmarkIsPalindrome(b *testing.B) {
    for i := 0; i < b.N; i++ {
        IsPalindrome("A man, a plan, a canal: Panama")
    }
}

默认情况下不运行任何基准测试。我们需要通过-bench命令行标志参数手工指定要运行的基准测试函数。该参数是一个正则表达式,用于匹配要执行的基准测试函数的名字,默认值是空的。其中“.”模式将可以匹配所有基准测试函数

-benchmem命令行标志参数将在报告中包含内存的分配数据统计

对于一些更微妙的问题,你可能需要使用pprof的图形显示功能。这个需要安装GraphViz工具,可以从 http://www.graphviz.org 下载

示例函数

以Example为函数名开头

在go test执行测试的时候也会运行示例函数测试。如果示例函数内含有类似上面例子中的// Output:格式的注释,那么测试工具会执行这个示例函数,然后检查示例函数的标准输出与注释是否匹配。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment