Go 语言单元测试 - 入门篇
go 语言发展非常迅速,大家对go语言编程也比较熟悉了,但很多同学对于go语言的测试不太熟悉,最近准备三篇关于Go语言的测试文章来介绍Go语言的测试相关内容。
单元测试框架
go语言提供了编写go语言的自动化测试框架testing.T,testing.B等,我们通过go test命令就可以来启动测试。我们编写测试方法的格式如下:
func TestXxx(*testing.T)
注意这里的方法名TestXxx中第一个X是大写,这是一种推荐的规范。测试方法名在实际方法名Xxx前加Test标明这是测试Xxx的方法。
另外,我们推荐将测试文件取名xxx_test.go这样的格式,其中xxx为go源文件的名称,方便我们定位某个文件的测试文件。go测试文件和源文件放在同一个目录下。该文件将从常规软件包构建中排除,但在运行go test命令时将包含该文件。
单元测试编写规范
单元测试通常都是通过一组用例数据,作为方法的参数传入,检查输出是否与预期的返回值相同。这种思路也适用于Go语言,只不过在实现形式上推荐采用更为优雅的方式。
假设我们有一个非常简单的helper.go, 实现两个整数相加输出结果的功能,实现代码如下:
package helper func Add(a, b int) int { return a + b }
我们可以写如下的测试代码:
package helper import ( "testing" ) func TestAdd(t *testing.T) { var param1 = 1 var param2 = 1 var result = 2 r := Add(param1, param2) if r != result { if r != result { t.Errorf("error: expecte %d, but got %d", result, r) } } }
这个代码这样写并没有什么大的问题,如果考虑其扩展性,比如增加新的测试场景(代码在不断迭代优化中),代码的可读性就会变得越来越差,如何有效确保扩展性的前提下不降低可读性呢?推荐如下方式:
package helper import ( "testing" ) func TestAdd(t *testing.T) { tests := []struct { param1 int param2 int result int }{ { param1: 1, param2: 1, result: 2, }, } for _, test := range tests { r := Add(test.param1, test.param2) if r != test.result { t.Errorf("error: expecte %d, but got %d", test.result, r) } } }
特殊功能开关
指定运行的方式
可以通过go test -run xxx的形式来指定运行的方法,注意这里的run参数是正则匹配的,如下所示:
bogon:helper $ go test -v === RUN TestAdd --- PASS: TestAdd (0.00s) === RUN TestExp --- PASS: TestExp (0.00s) PASS ok demo/helper 0.005s bogon:helper $ go test -run TestAdd -v === RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok demo/helper 0.005s bogon:helper $ go test -run TestExp -v === RUN TestExp --- PASS: TestExp (0.00s) PASS ok demo/helper 0.005s
获取测试覆盖率
go 提供了获取单元测试覆盖率参数,执行如下命令:
go test -v -coverprofile cover.out go tool cover -html=cover.out -o cover.html
我们打开cover.html就可以获得单元测试覆盖率信息,如下所示:
限制同时执行的线程数
go语言有一个GOMAXPROCS变量,可以指定运行Go代码的操作系统线程数。如果执行时不加该参数,则就是当前所设置的GOMAXPROCS值。
bogon:helper $ go test -cpu 1 PASS ok demo/helper 0.005s
总结
Go语言是一个严禁且高效的开发语言,在单元测试方面,提供了很多好的工程实践,这些思想可以在其他语言中采用,这一篇是一个开始,后续会介绍Go语言测试的Benchmark部分,主要是针对性能相关部分的内容。