单元测试

寒江蓑笠翁大约 5 分钟

单元测试

单元测试是一个项目不可获取的部分,其重要性不言而喻,对于一些项目而言,测试往往也是重中之重。go中的testing包中提供了测试相关的操作,用于go项目的测试,任何一个gopher都应该掌握如何测试自己的代码。

使用

官方推荐将测试文件与源代码文件放在一起,目录以test命名,测试文件以_test.go结尾。

package utils

func AddSum(a, b int) int {
   return a + b
}
package test

import "testing"
import "GoProject/src/utils"

func TestSum(t *testing.T) {
   utils.AddSum(1, 2)
}
|--- utils
|      | - utils.go
|--- test
       | - utils_test.go

随后进入test目录下,打开命令行输入go test,该包下的所有测试用例都会被执行。

PASS
ok      GoProject/src/test      0.390s

go test -v可以输出每个测试用例的结果,-cover可以查看覆盖率

=== RUN   TestFunc
--- PASS: TestFunc (0.00s)
PASS
coverage: [no statements]
ok      GoProject/src/test      0.385s

同时也可以指定某一个测试用例 go -run TestSum,参数支持正则表达式

PASS
ok      GoProject/src/test      0.401s

子测试

func TestSum(t *testing.T) {
   t.Run("sub", func(t *testing.T) {
      utils.AddSum(1, 2)
   })

   t.Run("sub1", func(t *testing.T) {
      utils.AddSum(1, 2)
   })
}

帮助函数

测试中,部分重复的逻辑会抽离出来,形成一个公共的函数,这些函数在被调用出错时,显示的堆栈信息往往是公共函数的信息,这对于测试而言没有任何好处,使用go提供的帮助函数,在出错时会显示调用者的信息。

只需要在函数中加一句t.Helper(),这让报错更加准确,有助于定位错误。

func helper(t *testing.T)  {
	t.Helper()
	//后续写公共逻辑
}

钩子函数

在同一个测试文件中,倘若需要做一些初始化工作和一些完成后的工作,可以将这部分工作抽离出来写在setup()teardown()函数中。

func setup() {
   fmt.Println("setup")
}

func teardown() {
   fmt.Println("down")
}

func TestMain(m *testing.M) {
   setup()
   code := m.Run()
   teardown()
   os.Exit(code)
}

TestMain函数存在时,启动测试会直接调用TestMain(),调用m.Run()将会运行所有的测试用例,返回值是未通过的测试用例的个数。

基准测试

func BenchmarkTest(b *testing.B) {
	...
}

函数名必须以Benchmakr开头,参数为b *testing.B,执行基准测试时,需要添加-bench参数

 go test -benchmem -bench .
goos: windows
goarch: amd64
pkg: GoProject/src/test
cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
BenchmarkTest-16        1000000000             0 B/op          0 allocs/op
PASS
ok      GoProject/src/test      0.040s

结果含义

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

b.RunParallel()可以测试并发性能。

func BenchmarkTest(b *testing.B) {
   b.RunParallel(func(pb *testing.PB) {
      for pb.Next() {
         utils.AddSum(1, 2)
      }
   })
}
goos: windows
goarch: amd64
pkg: GoProject/src/test
cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
BenchmarkTest-16        1000000000               0.04531 ns/op         0 B/op          0 allocs/op
PASS
ok      GoProject/src/test      0.459s

官方文档 testing package - testing - Go Packagesopen in new window