testing 包
参考 Golang 官网文档 学习。
导入语句:import "testing"
概述
testing 包提供对 Go 包的自动测试。它适用于和 go test
命令协作,自动执行下面格式的函数
func TestXxx(*testing.T)
其中,Xxx 不是小写字母开头。这个函数名用于识别测试代码。
在这些函数中,使用 Error、Fail 或相关的方法来标记失败。
要写一个新的测试集,新建一个文件以 _test.go 结尾,其中包含上述的 TestXxx 函数。将此文件放在将要测试的同一包中。正常的包编译不会包含此文件,但是运行 go test
命令时会包含。查看更多细节,运行 go help test
和 go help testflag
。
一个简单的功能测试函数看起来像这样:
func TestAbs(t testing.T) {
got := Abs(-1)
if got != 1 {
t.Errorf("Abs(-1) = %d; want 1", got)
}
}
基准测试
下面格式的函数被当做基准测试,并且当 go test
命令提供 -bench 标记时会执行此函数。基准测试是顺序执行的。
func BenchmarkXxxx(*testing.B)
对 testing 标记的描述,查看 Testing flags。
一个简单的基准测试函数看起来像这样:
func BenchmarkHello(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("hello")
}
}
基准测试函数必须运行目标代码 b.N 次。在执行基准测试期间,会调整 b.N 直到基准测试函数持续时间足够长,认为是时间可靠的。输出 BenchmarkHello 10000000 282 ns/op
意味着这个循环以每次循环 282 纳秒的速度运行了 10000000 次。
如果一个基准测试在运行之前需要一些耗时的设置,可重置定时器:
func BenchmarkBigLen(b *testing.B) {
big := NewBig()
b.ResetTimer()
for i := 0; i < b.N; i++ {
big.Len()
}
}
如果一个基准测试需要并行测试性能,可以使用 RunParallel 辅助函数;这样的基准测试适用于和 go test -cpu 标识一起使用:
func BenchmarkTemplateParallel(b *testing.B) {
templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
b.RunParallel(func(pb *testing.PB) {
var buf bytes.Buffer
for pb.Next() {
buf.Reset()
templ.Execute(&buf, "World")
}
})
}
示例函数
testing 包也会运行和验证示例代码。示例函数可以包含一个总结性的行注释,以 “Output:” 开头,并且运行测试的是和这个函数的标准输出比较。(这个比较忽视开始和末尾的空格)。下面是一个示例代码的例子:
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
func ExampleSalutations() {
fmt.Println("hello, and")
fmt.Println("goodbye")
// Output:
// hello, and
// goodbye
}
注释前缀 “Unordered output:” 类似于 “Output:”,但是匹配任意的行顺序:
func ExamplePerm() {
for _, value := range Perm(4) {
fmt.Println(value)
}
// Unordered output: 4
// 2
// 1
// 3
// 0
}
没有输出注释的示例函数被编译但是不会被执行。
声明包、函数 F、类型 T 和作用于类型 T 的方法 M 的示例函数的命名如下:
func Example() { ... }
func ExampleF() { ... }
func ExampleT() { ... }
func ExampleT_M() { ... }
可通过增加一个不同的后缀到函数名字后面以支持对于一个包/类型/函数/方法的多个示例函数。后缀必须以小写字母开始。
func Example_suffix() { ... }
func ExampleF_suffix() { ... }
func ExampleT_suffix() { ... }
func ExampleT_M_suffix() { ... }
当文件包含一个单独的示例函数,及至少一个其他的函数、类型、变量或常数声明,且没有功能测试函数或者基准测试函数时,整个测试文件作为例子显示。
跳过测试
可在运行时调用 *T 或 *B 的 Skip 方法跳过功能测试或基准测试:
func TestTimeConsuming(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
// ...
}
子测试项目和子基准测试
T 和 B 的 Run 方法允许定义子测试项目和子基准测试,而不需要为每个子测试项目和子基准测试定义另外的函数。这使能使用类似表驱动的基准测试和创建分级测试。它也提供了一种方式来共享共用的设置和终止代码:
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
每个子测试项目和子基准测试有一个唯一的名字:结合顶层测试的名字以及传递给 Run 的名字的顺序,由斜线分隔,以及一个可选的尾随的序号以消除歧义。
传递给 -run 和 -bench 命令行标识符的参数是一个不固定的正则表达式,匹配了测试的名字。参数是多个斜线分隔的元素时,比如子测试,参数是自身(斜线分隔);表达式匹配每个名字元素。因为是不固定的,一个空的表达式匹配任意字符串。比如,使用 “matching” 表达 “谁的名字包含”:
go test -run '' # Run all tests.
go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar".
go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
子测试也可用于控制并行度。一个父测试只有在其子测试完成时才会完成。在这个例子中,所有测试去其他测试并行运行,且只与其他测试并行,而与可能定义的其他顶层测试无关。
func TestGroupedParallel(t *testing.T) {
for _, tc := range tests {
tc := tc // capture range variable
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
// ...
})
}
}
当程序超过 8192 个并行 goroutine 时,竞争检测器会杀掉程序,因此当运行并行测试且设置了 -race 标识时需要注意。
Run 只有在并行子测试结束才会返回,为一组并行测试之后的资源清理提供了一种方式。
func TestTeardownParallel(t *testing.T) {
// This Run will not return until the parallel tests finish.
t.Run("group", func(t *testing.T) {
t.Run("Test1", parallelTest1)
t.Run("Test2", parallelTest2)
t.Run("Test3", parallelTest3)
})
// <tear-down code>
}
Main
测试程序有时需要在测试之前或之后做一些额外的设置和清理。并且,测试程序有时需要控制哪些代码运行在主线程。为了满足这些需求和其他的场景,一个测试文件可以包含一个函数:
func TestMain(m *testing.M)
然后,生成的测试会调用 TestMain(m) 而不是直接运行测试。TestMain 在主的 goroutine 运行,且可以做调用 m.Run 前后所需的所有设置和清理。然后,它应该使用 m.Run 的结果调用 OS.Exit。当调用 TestMain 时,flag.Parse 还没有运行。如果 TestMain(包括这些测试包) 依赖命令行标识,应该显式调用 flag.Parse。
一个简单的 TestMain 的实现:
func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
os.Exit(m.Run())
}
索引
例子
子目录
名字 | 概述 |
---|---|
iotest | 实现了主要用于 testing 的 Reader 和 Writer |
quick | 实现了帮助黑盒测试的工具函数 |