Go语言本身集成了轻量级的测试框架,由go test命令和testing包组成。包含单元测试和压力测试,是保证我们编写健壮Golang程序的有效工具。
$ uname -a
Darwin 18.6.0 Darwin Kernel Version 18.6.0: Thu Apr 25 23:16:27 PDT 2019; root:xnu-4903.261.4~2/RELEASE_X86_64 x86_64
$ go version
go version go1.12.4 darwin/amd64
老规矩,我会用一个简单的示例演示go test的用法,让大家有一个直观的感受。
$ ls
my.go my_test.go
$ go test -v
=== RUN TestAbs
--- PASS: TestAbs (0.00s)
PASS
ok mytest 0.006s
my.go文件内容:
package mytest
func Abs(a int) int {
if a < 0 {
return -1*a
}
return a
}
my_test.go文件的内容:
package mytest
import "testing"
func TestAbs(t *testing.T) {
result := Abs(-1)
if result != 1 {
t.Errorf("Abs(-1) = %d; want 1", result)
}
}
可以看出,测试文件是以_test.go结尾的文件,包含函数名TestXXX的文件签名func (t *testing.T)。测试框架会按函数定义的顺序依次执行Test开头的函数。如果测试函数调用错误函数,比如t.Error,t.Fail,那么测试就会认为是失败的。但是不会强制退出,测试程序还是会接着往下运行,除非调用t.Fatal,就会打印错误信息,并且强制退出。go test会自动编译运行_test.go结尾的文件,go build不会编译_test.go结尾的文件,所以不用担心test文件和正式文件定义为同一个包导致包大小增大。
go test执行时会编译_test结尾的文件,执行文件中以Test,Benchmark,Example 开头的测试函数。建议_test.go文件和源文件声明为同一个包。单元测试函数TestXXX的参数是testing.T,BenchmarkXXX的参数是testing.B,函数内以b.N作为循环次数,其中N会动态变化。可以通过testing.T的Error,Errorf,Fail,Fatal,Fatalf 来说明测试不通过。
上述的示例单元测试用了-1这个测试用例来演示测试用法,但是在实际生产中我们编写一个测试函数肯定会有多种测试用例,各种边界条件,那这种情况应该怎么办呢?难道要每个测试用例拷贝一份上述的测试函数吗,显然是不合适的。这就介绍接下来的表格驱动测试(table driven test):
package mytest
import "testing"
func TestAbs(t *testing.T) {
var testcases = []struct {
in int
out int
}{
{-1, 1},
{0, 0},
{2, 2},
{123, 123},
{-30, -30},
}
for _, tc := range testcases {
result := Abs(tc.in)
if result != tc.out {
t.Fatalf("Abs(%d) = %d; want %d", tc.in, result, tc.out)
}
}
}
表格的每一项是一个完整的测试用例,包含输入信息和期望的输出结果,有时候还包含测试用例名称等额外信息。这样我们就可以在表格里面编写各种测试用例场景,包括常规场景,边界场景,和一些异常场景。运行结果为:
$ go test -v
=== RUN TestAbs
--- FAIL: TestAbs (0.00s)
my_test.go:19: Abs(-30) = 30; want -30
FAIL
exit status 1
FAIL mytest 0.005s
性能测试函数以是Benchmark开头,格式为:
func Benchmark(b *testing.B)
go test 默认是不会运行_test.go文件中的Benchmark函数,需要通过-bench选项开启。演示代码:
package mytest
import "testing"
func BenchmarkAbs(b *testing.B) {
for i := 0; i < b.N; i++ {
result := Abs(i)
if result != i {
b.Errorf("benchmark faild, abs(%d) result %d, except %d", i, result, i)
}
}
}
运行结果:
$ go test -bench=".*"
goos: darwin
goarch: amd64
pkg: mytest
BenchmarkAbs-12 2000000000 0.26 ns/op
PASS
ok mytest 0.553s
-bench后面跟的是正则表达式,我们可以指定需要匹配运行的benchmark函数,示例中的.*表示运行所有的benchmark函数。
上述结果显示总共运行了0.553s,一共运行了2000000000次,平均每次运行的时间是0.26纳秒。
文章介绍了Go语言自带的go test测试框架的单元测试和性能测试方法。下篇将会讲解gomock模拟实际的生产接口。
https://golang.org/pkg/testing/