1 - go testing 包概述
资料
教程
2 - go testing 包文档
备注:https://golang.org/pkg/testing/
概述
Package testing 为Go package的自动测试提供支持。它旨在与 “go test “命令协同使用,该命令可以自动执行任何如下形式的函数:
func TestXxx(*testing.T)
其中Xxx不以小写字母开头。函数名称的作用是识别测试routine。
在这些函数中,使用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)
}
}
Benchmarks
这种形式的函数:
func BenchmarkXxx(*testing.B)
被视为基准( benchmarks), 并在提供 -bench 标志时由 “go test “命令执行。基准是按顺序运行的。
testing flags的描述, 请见 https://golang.org/cmd/go/#hdr-Testing_flags
基准函数的例子是这样的。
func BenchmarkRandInt(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Int()
}
}
基准函数必须运行目标代码 b.N 次。在基准执行过程中,b.N会被调整,直到基准函数持续足够长的时间来可靠地计时。输出
BenchmarkRandInt-8 68453040 17.8 ns/op
意味着循环运行68453040次,每次循环速度为17.8ns。
如果基准在运行前需要一些昂贵的设置,计时器可能需要被重置:
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")
}
})
}
Examples
该软件包还可以运行和验证example代码。example函数可能包含一个以 “Output: “开头的结尾行注释,并在测试运行时与函数的标准输出进行比较。(比较会忽略前导空格和后导空格。)这些都是example的例子。
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(5) {
fmt.Println(value)
}
// Unordered output: 4
// 2
// 1
// 3
// 0
}
没有输出注释的Example函数会被编译但不会被执行。
为包、函数F、类型T和类型T上的方法M声明例子的命名惯例是:
func Example() { ... }
func ExampleF() { ... }
func ExampleT() { ... }
func ExampleT_M() { ... }
包/类型/函数/方法的多个example函数,可以通过在名称后添加一个不同的后缀来提供。后缀必须以小写字母开头。
func Example_suffix() { ... }
func ExampleF_suffix() { ... }
func ExampleT_suffix() { ... }
func ExampleT_M_suffix() { ... }
当整个测试文件包含一个example函数,至少一个其他函数、类型、变量或常量声明,并且没有测试或基准函数时,整个测试文件将作为example呈现。
Skipping
在运行时可以通过调用 *T
或 *B
的Skip方法来跳过测试或基准。
func TestTimeConsuming(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
...
}
Subtests and Sub-benchmarks
T和B的Run方法允许定义子测试(subtest)和子基准(sub-benchmark),而不必为每个子基准定义单独的函数。这使得像表格驱动(table-driven)的基准和创建分层测试这样的用途成为可能。它还提供了一种共享通用设置和拆卸代码的方法。
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
命令行标志的参数是一个与测试名称相匹配的 unanchored 正则表达式。对于有多个斜线分隔元素的测试,比如子测试,参数本身是斜线分隔的,表达式依次匹配每个名称元素。因为它是unanchored,所以空的表达式可以匹配任何字符串。例如,用 “matching “来表示 “whose name contains”。
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()
...
})
}
}
Race检测器会在程序超过8192个并发goroutines的情况下杀死程序,所以在设置 -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
有时,测试程序需要在测试前或测试后进行额外的 setup 或 teardown。有时,测试也需要控制哪些代码在主线程上运行。为了支持这些和其他情况,如果一个测试文件包含一个函数:
func TestMain(m *testing.M)
然后生成的测试将调用 TestMain(m) 而不是直接运行测试。TestMain 在主 goroutine 中运行,并且可以围绕对 m.Run 的调用进行任何必要的设置和拆卸。m.Run 将返回一个退出代码,这个代码可以传递给 os.Exit。如果 TestMain 返回,测试包装器将把 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())
}
3 - flag 参数
备注:https://golang.org/cmd/go/#hdr-Testing_flags
‘go test’ 命令既接受适用于 ‘go test’ 本身的标志,也接受适用于产生的测试二进制的标志。
其中有几个标志可以控制profiling,并写出适合 “go tool pprof “的执行profiling;运行 “go tool pprof -h “可以获得更多信息。pprof 的 –alloc_space, –alloc_objects, 和 –show_bytes 选项控制了信息的呈现方式。
go test识别的标志
下面的标志被 ‘go test’ 命令所识别,并控制任何测试的执行。
-bench regexp
只运行与正则表达式匹配的基准benchmarks。
默认情况下,不运行任何基准。
要运行所有基准,请使用 ‘-bench .’ 或 ‘-bench=.’。
正则表达式由无括号的斜杠(/)字符分割成正则表达式序列,如果有的话,基准的标识符的每个部分必须与序列中的相应元素匹配。
Possible parents of matches are run with b.N=1 to identify sub-benchmarks. For example, given -bench=X/Y, top-level benchmarks matching X are run with b.N=1 to find any sub-benchmarks matching Y, which are then run in full.
在b.N=1的情况下,运行可能的匹配parent,以确定子基准。例如,给定-bench=X/Y,用b.N=1运行与X匹配的顶层基准,找出与Y匹配的子基准,然后完整地运行。
-benchtime t
为每个基准运行足够的迭代,花费时间t,指定为 time.Duration(例如,-benchtime 1h30s)。
默认为1秒(1s)。
特殊语法Nx表示运行基准N次(例如,-benchtime 100x)。
-count n
运行每个测试和基准 n 次(默认 1)。
如果设置了 -cpu,则对每个 GOMAXPROCS 值运行 n 次。
示例总是运行一次。
-cover
启用覆盖率分析。
请注意,由于覆盖率是通过在编译前对源代码进行注释来实现的,因此在启用覆盖率后,编译和测试失败时可能会报告与原始源代码不一致的行数。
-covermode set,count,atomic
设置正在测试的软件包的覆盖率分析模式,默认为 “set”,除非启用 -race,这种情况下默认为 “atomic”。
The values:
- set: bool: does this statement run?
- count: int: how many times does this statement run?
- atomic: int: count, but correct in multithreaded tests; significantly more expensive.
设置 -cover.
-coverpkg pattern1,pattern2,pattern3
在每个测试中对匹配模式的软件包应用覆盖率分析。
默认情况下,每个测试只分析被测试的包。
请参阅 ‘go help packages’ 以了解软件包模式的描述。
设置 -cover.
-cpu 1,2,4
指定执行测试或基准的 GOMAXPROCS 值的列表。默认为GOMAXPROCS的当前值。
-failfast
第一次测试失败后,不要再开始新的测试。
-list regexp
列出与正则表达式匹配的测试、基准或示例。
不会运行任何测试、基准或示例。这将只列出顶层测试,不会显示子测试或子基准。不显示子测试或子基准。
-parallel n
允许并行执行调用 t.Parallel 的测试函数。
这个标志的值是同时运行的测试的最大数量;默认情况下,它被设置为GOMAXPROCS的值。
请注意,-parallel只适用于单个测试二进制文件中。
根据 -p 标志的设置, ‘go test’ 命令也可以并行运行不同包的测试。 (见’go help build’)。
-run regexp
只运行那些与正则表达式相匹配的测试和示例。
对于测试来说,正则表达式被无括号的斜杠(/)字符分割成正则表达式序列,如果有的话,测试的标识符的每一部分必须与序列中的相应元素相匹配。
Note that possible parents of matches are run too, so that -run=X/Y matches and runs and reports the result of all tests matching X, even those without sub-tests matching Y, because it must run them to look for those sub-tests.
注意,匹配的可能的parent也会被运行,所以-run=X/Y匹配并运行和报告所有匹配X的测试的结果,甚至那些没有子测试匹配Y的测试,因为它必须运行它们来寻找这些子测试。
-short
告诉长期运行的测试缩短其运行时间。
默认情况下是关闭的,但在 all.bash 期间进行设置,这样安装Go树时就可以运行理智性检查,但不会花时间运行详尽的测试。
-timeout d
如果一个测试二进制文件运行时间超过持续时间d,就会产生恐慌/panic。
-v
Verbose输出:在测试运行时记录所有测试。即使测试成功,也会打印所有来自Log和Logf调用的文本。
-vet list
配置在 “go test “期间对 “go vet " 的调用,以使用逗号分隔的vet检查列表。
如果列表为空,“go test “就会使用一个被认为总是值得处理的精选检查列表来运行 “go vet”。
如果列表为 “off”,“go test “根本就不运行 “go vet”。
描述测试的标记
下面的标志也是’go test’所能识别的,并且可以在执行过程中用来描述测试。
-benchmem
为基准测试打印内存分配统计数据。
-blockprofile block.out
当所有测试完成后,将 goroutine blocking profile写入指定文件。
像 -c 那样写入测试二进制文件。
-blockprofilerate n
通过携带 n 调用 runtime.SetBlockProfileRate 来控制goroutine阻塞配置文件中提供的细节。
参见’go doc runtime.SetBlockProfileRate’。
profiler的目标是,平均每 n 纳秒对程序被阻塞的时间进行一次阻塞事件采样。默认情况下,如果设置了-test.blockprofile而没有这个标志,则会记录所有阻塞事件,相当于-test.blockprofilerate=1。
The profiler aims to sample, on average, one blocking event every n nanoseconds the program spends blocked. By default, if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1.
-coverprofile cover.out
Write a coverage profile to the file after all tests have passed.
Sets -cover.
-cpuprofile cpu.out
Write a CPU profile to the specified file before exiting. Writes test binary as -c would.
-memprofile mem.out
Write an allocation profile to the file after all tests have passed. Writes test binary as -c would.
-memprofilerate n
Enable more precise (and expensive) memory allocation profiles by setting runtime.MemProfileRate. See ‘go doc runtime.MemProfileRate’.
To profile all memory allocations, use -test.memprofilerate=1.
-mutexprofile mutex.out
Write a mutex contention profile to the specified file
when all tests are complete.
Writes test binary as -c would.
-mutexprofilefraction n
Sample 1 in n stack traces of goroutines holding a contended mutex.
-outputdir directory
将profiling的输出文件放置在指定的目录中,默认是 “go test “运行的目录。
-trace trace.out
在退出前向指定文件写入执行跟踪。
其他使用细节
这些标志中的每一个都可以用可选的 ’test.’ 前缀来识别,就像 -test.v 一样。然而,当直接调用生成的测试二进制文件 (‘go test -c’的结果)时,前缀是必须的。
在调用测试二进制文件之前,‘go test’命令会适当地重写或删除可选包列表之前和之后的识别标志。
例如,该命令
go test -v -myflag testdata -cpuprofile=prof.out -x
将编译测试二进制文件,然后以
pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
运行。
(-x 标志被删除了,因为它只适用于 go 命令的执行,而不是测试本身。)
生成profile文件的test flag (除了覆盖率) 也会将测试二进制文件留在 pkg.test 中,供分析profile文件时使用。
当 ‘go test’ 运行一个测试二进制文件时,它是在相应软件包的源代码目录下进行的。根据测试的不同,当直接调用生成的测试二进制文件时,可能需要做同样的工作。
命令行包列表,如果存在的话,必须出现在任何不为go测试命令所知的标志之前。继续上面的例子,软件包列表必须出现在-myflag之前,但也可以出现在-v的任何一边。
当’go test’以包列表模式运行时,‘go test’会缓存成功的包测试结果,以避免不必要的重复运行测试。要禁用测试缓存,请使用除可 cacheable 标志以外的任何测试标志或参数。明确禁止测试缓存的习惯方法是使用 -count=1。
为了不让测试二进制文件的参数被解释为已知的标志或包名,使用 -args (参见 ‘go help test’),它将命令行的其余部分不加解释和修改地传递给测试二进制文件。
例如,命令
go test -v -args -x -v
将编译测试二进制文件,然后以
pkg.test -test.v -x -v
运行。类似的
go test -args math
将编译测试二进制文件,然后以
pkg.test math
运行。
在第一个例子中,-x和第二个-v被传递到测试二进制文件中,没有变化,对go命令本身没有影响。在第二个例子中,参数math被传递到测试二进制文件中,而不是解释为包列表。