第11章 测试
软件测试是一个巨大的领域,但是Go语言的测试技术是相对比较低级的,它依赖一个Go test测试命令和一组按照约定方式编写的测试函数,测试命令可以运行这些函数
在实践中,编写测试代码和编写程序本身并没有多大区别
11.3 测试覆盖率
测试永远不可能是覆盖所有情况
本节,我们使用 go test命令中集成的测试覆盖率工具,来度量下面的测试覆盖率,帮助我们识别测试和期望之间的差距
如下代码是一个表格驱动的测试
func TestCoverage(t *testing.T) {
var tests = []struct {
input string
env Env
want string // expected error from Parse/Check or result from Eval
}{
{"x % 2", nil, "unexpected '%'"},
{"!true", nil, "unexpected '!'"},
{"log(10)", nil, `unknown function "log"`},
{"sqrt(1, 2)", nil, "call to sqrt has 2 args, want 1"},
{"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
{"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"},
{"5 / 9 * (F - 32)", Env{"F": -40}, "-40"},
}
for _, test := range tests {
expr, err := Parse(test.input)
if err == nil {
err = expr.Check(map[Var]bool{})
}
if err != nil {
if err.Error() != test.want {
t.Errorf("%s: got %q, want %q", test.input, err, test.want)
}
continue
}
got := fmt.Sprintf("%.6g", expr.Eval(test.env))
if got != test.want {
t.Errorf("%s: %v => %s, want %s",
test.input, test.env, got, test.want)
}
}
}
首先,我们要确保所有的测试都正常通过:
$ go test -v -run=Coverage gopl.io/ch7/eval
=== RUN TestCoverage
--- PASS: TestCoverage (0.00s)
PASS
ok gopl.io/ch7/eval 0.011s
使用显示覆盖率工具
go tool cover
Usage of 'go tool cover':
Given a coverage profile produced by 'go test':
go test -coverprofile=c.out
Open a web browser displaying annotated source code:
go tool cover -html=c.out
...
go tool 命令运行Go工具链的底层可执行程序。这些底层可执行程序放在
G
O
R
O
O
T
/
p
k
g
/
t
o
o
l
/
GOROOT/pkg/tool/
GOROOT/pkg/tool/{GOOS}_${GOARCH}目录。因为有go build 命令的原因,我们很少直接调用这些底层工具。
现在我们可以用-coverprofile 标志参数重新运行测试:
$ go test -run=Coverage -coverprofile=c.out gopl.io/ch7/eval
ok gopl.io/ch7/eval 0.032s coverage: 68.5% of statements
这个标志参数通过在测试代码中插入生成钩子来统计覆盖率数据,也就是说,在运行每个测试前,它将待测代码拷贝一份并做修改,在每个词法块都会设置一个布尔标志变量。当被修改后的被测试代码运行退出时,将统计日志数据写入c.out文件,并打印一部分执行的语句的一个总结。(如果你需要的是摘要,使用go test -cover )
如果使用了-covermode=count 标志参数,那么将在每个代码块插入一个计数器而不是布尔标志量。在统计结果中记录了每个块的执行次数,这可以用于衡量哪些是被频繁执行的热点代码
为了收集数据,我们运行了测试覆盖率工具,打印了测试日志,生成一个HTML报告,然后在浏览器中打开
go tool cover -html=c.out
实现100%的测试覆盖率听起来很美,但是在具体实践中通常是不可行的,也不是值得推荐的做法
|