golang单元测试攻略


1 Go测试的分类

  1. 单元测试

  2. 基准测试(性能测试)

  3. 子测试与子基准测试

2 测试模式

包列表模式:go test . 测试在命令上列出的每个包。正则匹配方式。

当前目录模式:go test

3 常用参数和示例

这里介绍几个常用的参数:

  • -bench regexp 执行相应的 benchmarks,例如 -bench=.
  • -cover 开启测试覆盖率;
  • -run regexp 只运行 regexp 匹配的函数
    • 例如 -run=TestAdd(-run TestAdd) 那么就执行包含有 TestAdd 开头的函数,'TestAdd$'代表只执行Array名字的函数
    • 参数支持通配符 *,和部分正则表达式,例如 ^$
    • -run=none,代表要执行这个名字的函数,如果没有就不执行,也就代表不执行单元测试
  • -v 显示测试的详细命令。
    • go test -v-v 参数会显示每个用例的测试结果
  • -benchtime :可以自定义测试时间

go test,该 package 下所有的文件的所有测试用例都会被执行。

go test calc_test.go,运行该 package 下的calc_test.go文件的所有测试用例。

go test calc_test.go -run=TestAdd,运行该 package 下的calc_test.go文件的以TestAdd开头的所有用例。

go test calc_test.go -run='TestAdd$',运行该 package 下的calc_test.go文件的TestAdd用例。

4 测试代码规范

执行 go test 命令,它会在 *_test.go 中寻找 test 测试benchmark 基准examples 示例 函数。测试函数必须以 TestXXX 的函数名出现(XXX 为以非小写字母开头),基准函数必须以 BenchmarkXXX 的函数名出现,示例函数必须以 ExampleXXX 的形式。三种函数类似下面的签名形式:

// test 测试函数
func TestXXX(t *testing.T) { ... }

// benchmark 基准函数
func BenchmarkXXX(b *testing.B) { ... }

// examples 示例函数,其相关命名方式可以查看第一篇文章
func ExamplePrintln() {
    Println("The output of\nthis example.")
    // Output: The output of
    // this example.
}
或
func ExamplePerm() {
    for _, value := range Perm(4) {
        fmt.Println(value)
    }

    // Unordered output: 4
    // 2
    // 1
    // 3
    // 0
}

Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 AddMul 函数,那么应该新建 calc_test.go 作为测试文件。

example/
   |--calc.go
   |--calc_test.go

假如 calc.go 的代码如下:

package main

func Add(a int, b int) int {
    return a + b
}

func Mul(a int, b int) int {
    return a * b
}

那么 calc_test.go 中的测试用例可以这么写:

package main

import "testing"

func TestAdd(t *testing.T) {
    if ans := Add(1, 2); ans != 3 {
        t.Errorf("1 + 2 expected be 3, but %d got", ans)
    }

    if ans := Add(-10, -20); ans != -30 {
        t.Errorf("-10 + -20 expected be -30, but %d got", ans)
    }
}
  • 测试用例名称一般命名为 Test 加上待测试的方法名。
  • 测试用的参数有且只有一个,在这里是 t *testing.T

5 单元测试日志级别

每个测试用例可能并发执行,使用 testing.T 提供的日志输出可以保证日志跟随这个测试上下文一起打印输出。testing.T 提供了几种日志输出方法,详见下表所示。

方 法 备 注
Log 打印日志,同时结束测试
Logf 格式化打印日志,同时结束测试
Error 打印错误日志,同时结束测试
Errorf 格式化打印错误日志,同时结束测试
Fatal 打印致命日志,同时结束测试
Fatalf 格式化打印致命日志,同时结束测试

6 单元测试

  • 测试用例名称一般命名为 Test 加上待测试的方法名。
  • 测试用的参数有且只有一个,在这里是 t *testing.T

7 基准测试

基准测试(benchmark)的参数是 *testing.B,TestMain 的参数是 *testing.M 类型。

代码结构

example/
   |--calc.go
   |--calc_test.go
   |--benchmark_test.go

7.1 代码运行和解释

import "testing"

func BenchmarkAdd(b *testing.B) {
   var n int
   for i := 0; i < b.N; i++ {
      n++
   }
}

func BenchmarkMul(b *testing.B) {
   var n int
   for i := 0; i < b.N; i++ {
      n++
   }
}
> ~/g/s/b/testing go test -v -bench=. benchmark_test.go                                                                                                           
goos: darwin
goarch: amd64
BenchmarkAdd
BenchmarkAdd-12         1000000000               0.263 ns/op
BenchmarkMul
BenchmarkMul-12         1000000000               0.255 ns/op
PASS
ok      command-line-arguments  0.665s
  1. -bench=.表示运行 benchmark_test.go 文件里的所有基准测试,和单元测试中的-run类似。
  2. 第5行解释
    1. BenchmarkAdd-12 :显示基准测试名称
    2. 1000000000 :表示测试的次数,也就是 testing.B 结构中提供给程序使用的 N。
    3. “0.263 ns/op”表示每一个操作耗费多少时间(纳秒)。
  3. -benchtime参数可以自定义测试时间

1 秒 = 1000000000 纳秒

1秒(s) =1000毫秒(ms)
1毫秒(ms)=1000微秒 (us)
1微秒(us)=1000纳秒 (ns)

基准测试报告每一列值对应的含义如下:

type BenchmarkResult struct {
    N         int           // 迭代次数
    T         time.Duration // 基准测试花费的时间
    Bytes     int64         // 一次迭代处理的字节数
    MemAllocs uint64        // 总的分配内存的次数
    MemBytes  uint64        // 总的分配内存的字节数
}

如果基准测试需要在并行设置中测试性能,则可以使用 RunParallel 辅助函数 ; 这样的基准测试一般与 go test -cpu 标志一起使用:

通过 RunParallel 方法能够并行地执行给定的基准测试。RunParallel会创建出多个 goroutine,并将 b.N 分配给这些 goroutine 执行,其中 goroutine 数量的默认值为 GOMAXPROCS。用户如果想要增加非 CPU 受限(non-CPU-bound)基准测试的并行性,那么可以在 RunParallel 之前调用 SetParallelism(如 SetParallelism(2),则 goroutine 数量为 2*GOMAXPROCS)。RunParallel 通常会与 -cpu 标志一同使用。

func BenchmarkTemplateParallel(b *testing.B) {
    templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
    b.RunParallel(func(pb *testing.PB) {
        // 每个 goroutine 有属于自己的 bytes.Buffer.
        var buf bytes.Buffer
        for pb.Next() {
            // 循环体在所有 goroutine 中总共执行 b.N 次
            buf.Reset()
            templ.Execute(&buf, "World")
        }
    })
}
> ~/g/s/b/testing go test -v -bench=BenchmarkTemplateParallel benchmark_test.go                                                                                   
goos: darwin
goarch: amd64
BenchmarkTemplateParallel
BenchmarkTemplateParallel-12             5213426               236 ns/op
PASS
ok      command-line-arguments  2.687s

通过-benchtime参数可以自定义测试时间

> ~/g/s/b/testing go test -v -bench=. -benchtime=5s benchmark_test.go                                                                                             
goos: darwin
goarch: amd64
BenchmarkAdd
BenchmarkAdd-12                         1000000000               0.257 ns/op
BenchmarkMul
BenchmarkMul-12                         1000000000               0.258 ns/op
BenchmarkTemplateParallel
BenchmarkTemplateParallel-12            27029740               228 ns/op
PASS
ok      command-line-arguments  8.157s
> ~/g/s/b/testing go test -v -bench=.  benchmark_test.go                                                                                                          
goos: darwin
goarch: amd64
BenchmarkAdd
BenchmarkAdd-12                         1000000000               0.246 ns/op
BenchmarkMul
BenchmarkMul-12                         1000000000               0.255 ns/op
BenchmarkTemplateParallel
BenchmarkTemplateParallel-12             4983981               232 ns/op
BenchmarkFib1
BenchmarkFib1-12                        731937631                1.66 ns/op
BenchmarkFib2
BenchmarkFib2-12                        262092915                4.45 ns/op
BenchmarkFib3
BenchmarkFib3-12                        157617190                7.47 ns/op
BenchmarkFib10
BenchmarkFib10-12                        4234692               297 ns/op
BenchmarkFib20
BenchmarkFib20-12                          34222             35516 ns/op
BenchmarkFib40
BenchmarkFib40-12                              2         529998057 ns/op
PASS
ok      command-line-arguments  12.166s
⋊> ~/g/s/b/testing go test -bench=. -run=none -benchmem                                                                                                            15:19:06
BenchmarkXXX
goos: darwin
goarch: amd64
pkg: basic_go/testing
BenchmarkXXX-12                         BenchmarkXXX
BenchmarkXXX
BenchmarkXXX
BenchmarkXXX
BenchmarkXXX
1000000000               0.000023 ns/op        0 B/op          0 allocs/op
BenchmarkAdd-12                         1000000000               0.260 ns/op           0 B/op          0 allocs/op
BenchmarkMul-12                         1000000000               0.259 ns/op           0 B/op          0 allocs/op
BenchmarkTemplateParallel-12             5424362               236 ns/op             272 B/op          8 allocs/op
BenchmarkFib1-12                        695810144                1.65 ns/op            0 B/op          0 allocs/op
BenchmarkFib2-12                        262647230                4.62 ns/op            0 B/op          0 allocs/op
BenchmarkFib3-12                        153866595                7.74 ns/op            0 B/op          0 allocs/op
BenchmarkFib10-12                        4036820               315 ns/op               0 B/op          0 allocs/op
BenchmarkFib20-12                          32605             36246 ns/op               0 B/op          0 allocs/op
BenchmarkFib40-12                              2         543972525 ns/op               0 B/op          0 allocs/op
PASS
ok      basic_go/testing        13.141s

7.2 分析基准测试数据

  • cpu 使用分析:-cpuprofile=cpu.pprof
  • 内存使用分析:-benchmem -memprofile=mem.pprof
  • block分析:-blockprofile=block.pprof

在配合 pprof 就可以进行分析。

运行命令采样数据:

go test -bench=. -run=none -benchmem -memprofile=mem.pprof
go test -bench=. -run=none -blockprofile=block.pprof

go test -bench=. -run=none -benchmem -memprofile=mem.pprof -cpuprofile=cpu.pprof

7.3 基准测试原理

基准测试框架对一个测试用例的默认测试时间是 1 秒。开始测试时,当以 Benchmark 开头的基准测试用例函数返回时还不到 1 秒,那么 testing.B 中的 N 值将按 1、2、5、10、20、50……递增,同时以递增后的值重新调用基准测试用例函数。

8 参考

https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter09/09.0.html

http://c.biancheng.net/view/124.html

https://geektutu.com/post/quick-go-test.html#4-%E5%B8%AE%E5%8A%A9%E5%87%BD%E6%95%B0-helpers

https://www.cnblogs.com/jiujuan/p/14604609.html


文章作者: wish
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wish !
评论
评论
  目录