上篇文章讨论了如何使用testing标准包进行自动化测试,在这篇文章中将进一步细化测试覆盖率的可视化确认。
测试覆盖率
代码测试的覆盖率分很多种,语句覆盖/条件覆盖等等,而在go中,测试覆盖率是这样定义的:Test coverage is a term that describes how much of a package’s code is exercised by running the package’s tests. 可以看出,go语言中的理解是:测试覆盖率是通过执行某包的测试用例来确认代码被执行的程度的术语。
项目 | URL |
---|---|
Cover | https://blog.golang.org/cover |
统计模式
在go语言的测试覆盖率统计时,go test通过参数covermode的设定可以对覆盖率统计模式作如下三种设定。
模式 | 解释 |
---|---|
set | 缺省模式, 只记录语句是否被执行过 |
count | 记录语句被执行的次数 |
atomic | 记录语句被执行的次数,并保证在并发执行时的正确性 |
测试对象
项目 | 详细 | 说明 |
---|---|---|
文件名 | basicfunc.go | 测试对象文件 |
package名 | basicfunc | 测试对象Package |
测试对象函数 | GetGrade | 输入分数返回A-D的等级 |
测试对象函数 | Add | 输入两个数字,返回其相加之和 |
示例代码
[root@liumiaocn test]# cat basicfunc.go
package basicfunc
func GetGrade(score int) string {
switch {
case score < 60:
return "D"
case score <= 70:
return "C"
case score <= 80:
return "B"
case score <= 90:
return "A"
default:
return "Undefined"
}
}
func Add(num1 int, num2 int) int {
return num1 + num2
}
[root@liumiaocn test]#
功能测试用例
[root@liumiaocn test]# cat func_test.go
package basicfunc
import "testing"
func TestBasic(test *testing.T) {
grade := GetGrade(40)
if grade != "D" {
test.Error("Test Case failed.")
}
}
func TestAddfunc(test *testing.T) {
sum := Add(1, 1)
if sum == 2 {
test.Log("Passed: 1 + 1 == 2 ")
} else {
test.Log("Failed: 1 + 1 == 2 ")
}
}
[root@liumiaocn test]#
覆盖率确认
注意事项
当go test命令无法正常运行时,学习时请将测试对象package放到GOROOT下,比如
[root@liumiaocn test]# echo $GOROOT
/usr/local/go
[root@liumiaocn test]# pwd
/usr/local/go/src/goprj/test
[root@liumiaocn test]#
确认覆盖率
[root@liumiaocn test]# go test -cover
PASS
coverage: 42.9% of statements
ok goprj/test 0.007s
[root@liumiaocn test]#
HTML方式
可以看到我们现在得到了一个42.9%的覆盖率,这说明42.9%的代码在测试用例执行中被至少执行过一次。但是想确认那些语句没有被执行的时候,上述方法就无法使用了。接下来我们将继续使用go test命令生成html文件用以显示测试覆盖率情况。
生成HTML前的准备
哪个文件的哪行代码被执行了的中间信息如果没有的话,无论什么工具也会无能为力。所以第一步就是要生成这些中间的信息。
[root@liumiaocn test]# pwd
/usr/local/go/src/goprj/test
[root@liumiaocn test]# go test -coverprofile=covprofile
PASS
coverage: 42.9% of statements
ok goprj/test 0.007s
[root@liumiaocn test]# cat covprofile
mode: set
goprj/test/basicfunc.go:3.33,4.16 1 1
goprj/test/basicfunc.go:5.9,6.27 1 1
goprj/test/basicfunc.go:7.9,8.27 1 0
goprj/test/basicfunc.go:9.9,10.27 1 0
goprj/test/basicfunc.go:11.9,12.27 1 0
goprj/test/basicfunc.go:13.9,14.35 1 0
goprj/test/basicfunc.go:18.34,20.2 1 1
[root@liumiaocn test]#
可以看到生成的中间文件covprofile中覆盖率统计模式为set,同时还有很多统计信息。
生成HTML文件
[root@liumiaocn test]# go tool cover -html=covprofile -o coverage.html
[root@liumiaocn test]#
生成的HTML文件内容
[root@liumiaocn test]# cat coverage.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">goprj/test/basicfunc.go (42.9%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">package basicfunc
func GetGrade(score int) string <span class="cov8" title="1">{
switch </span>{
<span class="cov8" title="1">case score < 60:
return "D"</span>
<span class="cov0" title="0">case score <= 70:
return "C"</span>
<span class="cov0" title="0">case score <= 80:
return "B"</span>
<span class="cov0" title="0">case score <= 90:
return "A"</span>
<span class="cov0" title="0">default:
return "Undefined"</span>
}
}
func Add(num1 int, num2 int) int <span class="cov8" title="1">{
return num1 + num2
}</span>
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
[root@liumiaocn test]#
结果确认
将生成的HTML用浏览器打开,可以很清楚地看到测试用例覆盖的代码和未曾覆盖到的代码,一目了然。
参考文献
项目 | URL |
---|---|
Cover | https://blog.golang.org/cover |