go test 上篇

前言

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/

https://github.com/golang/go/wiki/TableDrivenTests

https://golang.org/doc/code.html#Testing

 

你可能感兴趣的:(go test 上篇)