前言
之前在写GO
单元测试的时候, 使用了这个结构testing.T
. 进来无事翻了翻, 发现testing
包中还有一些其他的结构体, 想来是不同用处. 没想到GO
的testing
包竟然默默做了这么多支持, 之前竟然不知道.
在testing
包中包含一下结构体:
testing.T
: 这就是我们平常使用的单元测试testing.F
: 模糊测试, 可以自动生成测试用例testing.B
: 基准测试. 对函数的运行时间进行统计.testing.M
: 测试的钩子函数, 可预置测试前后的操作.testing.PB
: 测试时并行执行.
依次对GO
的各个测试类型进行介绍.
以下各项测试中出现的方法Reverse
如下:
// 此方法源自 Go 官方文档
func Reverse(s string) string {bs := []byte(s)length := len(bs)for i := 0; i < length/2; i++ {bs[i], bs[length-i-1] = bs[length-i-1], bs[i]}return string(bs)
}
testing.T
用于进行单元测试. 官方文档
Go
对单元测试函数要求如下:
- 文件名形如:
xxx_test.go
- 函数签名形如:
func TestXxx(t *testing.T)
我们创建文件lib_test.go
, 并在其中定义如下方法:
func TestReverse(t *testing.T) {str := "abc"revStr1 := Reverse(str)revStr2 := Reverse(revStr1)if str != revStr2 {// error 方法报错后, 会继续向下执行t.Error("error")// fatal 方法报错后, 会退出测试// t.Fatal("fatal")// 输出调试信息// t.Log("log")// 测试中断, 但是测试结果不会十遍// t.Skip("skip")}// 可启动多个子测试, 子测试之间并行运行for _, str = range []string{"abcd", "aceb"} {// 第一个参数为子测试的标识t.Run(str, func(t *testing.T) {revStr1 := Reverse(str)revStr2 := Reverse(revStr1)if str != revStr2 {t.Error("error")}})}
}
使用如下命令运行测试用例(test.run 指定运行某一个函数):
go test -test.run TestReverse
这就是单元测试的简单应用了, 是不是so easy
啦.
testing.F
用于模糊测试, 会自动生成测试用例. 官方文档
其内部会自动生成各种测试用例, 并自动调用执行. Go
对模糊测试的函数要求如下:
- 文件名形如:
xxx_test.go
- 函数签名形如:
func FuzzXxx(f *testing.F)
其测试函数定义如下:
func FuzzReverse(f *testing.F) {// 设置测试用例需要随机生成的变量类型f.Add("Hello, world!")// 生成测试用例并进行测试. 回电函数接收的参数, 与 f.Add 设置的参数类型一致f.Fuzz(func(t *testing.T, str string) {revStr1 := Reverse(str)revStr2 := Reverse(revStr1)if revStr2 != str {t.Error("error")}// 判断是否是合法的 utf8 编码if utf8.ValidString(str) && !utf8.ValidString(revStr1) {t.Error("utf8 error")}})
}
运行命令开始测试: go test -test.fuzz FuzzReverse -test.run ^$
(其中test.run
指定不运行test
函数)
当测试失败的时候, 失败的用力会写入指定的文件, 文件在控制台输出.
testing.B
用于基准测试. 对函数的运行时间进行统计. , 对函数要求如下:
- 文件名形如:
xxx_test.go
- 函数签名形如:
func BenchmarkXxx(b *testing.B)
函数定义如下:
func BenchmarkReverse(b *testing.B) {// 打开内存统计b.ReportAllocs()// 按照要求运行 n 遍for i := 0; i < b.N; i++ {Reverse("hello")}
}
运行命令: go test -test.bench BenchmarkReverse -test.run ^$
结果中指出了运行次数及平均时间. 其中各项值得含义如下:
-
100000000
: 迭代次数 -
ns/op
: 平均每次迭代消耗的时间 -
B/op
: 平均每次迭代消耗的内存 -
allocs/op
: 平均每次迭代内存的分配次数
testing.M
定义在运行测试的前后执行的操作. 对函数的要求如下:
- 文件名形如:
xxx_test.go
- 函数签名为:
func TestMain(m *testing.M)
函数定义如下:
func TestMain(m *testing.M) {// 测试之前执行的操作fmt.Println("starting test main")// 运行测试code := m.Run()// 测试之后执行的操作fmt.Println("ending test main")os.Exit(code)
}
此函数会在运行所有测试时自动调用.
testing.PB
用于在测试时进行并发测试. 上面的 单元测试/模糊测试/基准测试 都可以使用. 以基准测试为例, 使用如下:
// 充分利用 CPU 资源, 并行执行 n 次
func BenchmarkReverse2(b *testing.B) {b.RunParallel(func(pb *testing.PB) {for pb.Next() {// 此循环体总共执行 b.N 次Reverse("hello")}})
}
如此便可并行执行啦.
好, 有关Go
的单元测试, 到这里就差不多了. 以上这些已经基本能够满足日常使用了
原文链接: https://hujingnb.com/archives/798