Golang测试覆盖率
最近实习在做一些CI/CD的工作,用到了go的一些测试工具,学习记录一下。都是官网博客找的来源:https://blog.golang.org/cover
golang1.2引进了一种新的测试覆盖工具,采用一种特别的方法来生成覆盖率统计数据。
覆盖率测试
代码覆盖率测试是指,通过运行一个测试来查看有多少代码被执行了。如果使用的测试样例导致80%的源代码语句运行,我们就认为代码覆盖率为80%。
go语言引进了一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试。这套框架的原理很简单,就是在2变异之前重写package的源代码以添加检测、编译和运行修改后的源代码,并且将统计信息转移。
golang的覆盖率测试
假设有这样一个源代码:
1 | package size |
和这样的一个测试样例:
1 | package size |
然后使用go test命令加上-cover选项来测试代码覆盖率,产生结果如下:
1 | PASS |
- 由于go test命令只能在一个相应的目录下执行所有文件,注意这两个文件需要在同一个目录下。
- 另外测试文件必须要以_test.go结尾,这样在执行go test的时候才能执行到相应的代码。
- 在测试文件中必须要import testing这个包,另外所有测试函数都以Test开头,并且参数是testing.T,用来记录错误或者测试状态;格式为:
1 | func TestXxx (t *testing.T) //Xxx开头必须大写 |
上面的结果显示,我们代码的测试覆盖率为42.9%,这个数字怎么来的呢?当启用测试覆盖的时候,go test会用cover工具在编译之前重写源代码,在原始代码中寻找分支,然后在每个分支"种下"锚点。 等所有的case都跑完后,通过统计执行锚点的数量来计算覆盖率。比如变成这样:
1 | func Size(a int) string { |
虽然这样重写代码的方式开销看起来比较昂贵,但实际上它会被编译为单个的move指令,在实际测试时开销仅仅增加3%。
Although that annotating assignment might look expensive, it compiles to a single "move" instruction. Its run-time overhead is therefore modest, adding only about 3% when running a typical (more realistic) test. That makes it reasonable to include test coverage as part of the standard development pipeline.
查看结果
实际上,刚刚那种测试结果看起来比较简陋,我们需要了解更加详细的测试内容,因此我们可以使用go test来输出一个coverage profile,该文件保存着收集的统计信息的文件,使得我们研究它们更加方便。
1 | go test -coverprofile=coverage.out |
输出结果为:
1 | mode: set |
有了这么一个文件,我们可以不进行测试,直接查看测试结果:
1 | go tool cover -func=coverage.out |
热图
我们可以以多种方式来检测代码覆盖率,go test接受-covermode标志来将覆盖模式设置为以下三种:
- set:每个语句都运行了吗
- count:每个语句运行了多少次
- atomic:跟count一样,但能平行程序中精确计算(使用了来自sync/atomic的原子操作)
运行这样一行命令,会打开一个浏览器,注意查看绿色的强度如何变化。更明亮的绿色表示具有更高的执行次数。将鼠标停留在语句上,会显示具体的执行次数
1 | go tool cover -html=count.out |