test文件
在包目录内,所有以_test.go
为后缀名的源文件在执行go build时不会被构建成包的一部分。
在*_test.go
文件中,有三种类型的函数:测试函数、基准测试(benchmark)函数、示例函数。go test命令会遍历所有的*_test.go
文件中符合上述命名规则的函数,生成一个临时的main包用于调用相应的测试函数,接着构建并运行、报告测试结果,最后清理测试中生成的临时文件。
测试函数
测试函数是以Test为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确;go test命令会调用这些测试函数并报告测试结果是PASS或FAIL。
测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头:
func TestSin(t *testing.T) { /* ... */ }
其中t参数用于报告测试失败和附加的日志信息。
举例
新建文件main_test.go:
package word
import "testing"
func IsPalindrome(s string) bool {
for i := range s {
if s[i] != s[len(s)-1-i] {
return false
}
}
return true
}
func TestPalindrome(t *testing.T) {
if !IsPalindrome("kayak") {
t.Error(`IsPalindrome("kayak") = false`)
}
}
func TestNonPalindrome(t *testing.T) {
if IsPalindrome("abc") {
t.Error(`IsPalindrome("abc") = true`)
}
}
命令行输入:go test
输出:
PASS
ok packageStudy/test 0.053s
-v可用于打印每个测试函数的名字和运行时间。命令行输入:go test -v
输出:
=== RUN TestPalindrome
--- PASS: TestPalindrome (0.00s)
=== RUN TestNonPalindrome
--- PASS: TestNonPalindrome (0.00s)
PASS
ok packageStudy/test 0.053s
-run对应一个正则表达式,名称匹配的测试函数才会被go test测试命令运行:go test -v -run "Pa"
输出:
=== RUN TestNonPalindrome
--- PASS: TestNonPalindrome (0.00s)
PASS
ok packageStudy/gopsutil 0.051s
基准测试
基准测试函数是以Benchmark为函数名前缀的函数,它们用于衡量一些函数的性能;go test命令会多次运行基准函数以计算一个平均的执行时间。
基准测试是测量一个程序在固定工作负载下的性能。在Go语言中,基准测试函数和普通测试函数写法类似,但是以Benchmark为前缀名,并且带有一个*testing.B
类型的参数;*testing.B
参数除了提供和*testing.T
类似的方法,还有额外一些和性能测量相关的方法。它还提供了一个整数N,用于指定操作执行的循环次数。
下面是IsPalindrome函数的基准测试,其中循环将执行N次。
func BenchmarkIsPalindrome(b *testing.B) {
for i := 0; i < b.N; i++ {
IsPalindrome("A man, a plan, a canal: Panama")
}
}
命令行输入:go test -bench=.
//增加显示内存分配情况可用-benchmem:go test -bench=. -benchmem
输出:
goos: windows
goarch: amd64
pkg: packageStudy/test
BenchmarkIsPalindrome-4 300000000 4.22 ns/op
PASS
ok packageStudy/gopsutil 1.766s
和普通测试不同的是,默认情况下不运行任何基准测试。通过-bench
命令行标志参数指定要运行的基准测试函数。该参数是一个正则表达式,默认值是空的。其中“.”模式将可以匹配所有基准测试函数,此处也可-bench=IsPalindrome
。
结果中基准测试名的数字后缀部分,这里是4,表示运行时对应的GOMAXPROCS的值,这对于一些与并发相关的基准测试是重要的信息。报告显示每次调用IsPalindrome函数花费4.22纳秒,是执行300000000次的平均时间。执行次数根据每次执行时间而自动调整。
现在我们有了一个基准测试和普通测试,我们可以很容易测试改进程序运行速度的想法。也许最明显的优化是在IsPalindrome函数中第二个循环的停止检查,这样可以避免每个比较都做两次:
性能剖析
只需要开启下面其中一个标志参数就可以生成各种分析文件。当同时使用多个标志参数时需要当心,因为一项分析操作可能会影响其他项的分析结果。
$ go test -cpuprofile=cpu.out //CPU剖析数据标识了最耗CPU时间的函数
$ go test -blockprofile=block.out //阻塞剖析则记录阻塞goroutine最久的操作,例如系统调用、管道发送和接收,还有获取锁等。
$ go test -memprofile=mem.out //堆剖析则标识了最耗内存的语句。
示例函数
示例函数是以Example为函数名前缀的函数,提供一个由编译器保证正确性的示例文档。示例函数没有函数参数和返回值。
示例函数主要作用是作为文档:示例函数和注释并不一样,示例函数是真实的Go代码,需要接受编译器的编译时检查。
根据示例函数的后缀名部分,godoc这个web文档服务器会将示例函数关联到某个具体函数或包本身,因此ExampleIsPalindrome示例函数将是IsPalindrome函数文档的一部分,Example示例函数将是包文档的一部分。
在go test
执行测试的时候也会运行示例函数测试。如果示例函数内含有类似上面例子中的// Output:
格式的注释,那么测试工具会执行这个示例函数,然后检查示例函数的标准输出与注释是否匹配。格式如下:
func ExampleIsPalindrome() {
fmt.Println(IsPalindrome("A man, a plan, a canal: Panama"))
fmt.Println(IsPalindrome("palindrome"))
// Output:
// true
// false
}