标签:com out ase src alt 格式化 精确 idp intro
原文链接 the cover story
Go 语言内置了许多工具,比如godoc (可以根据你在注释中的内容生成介绍文档,注释格式参照,godoc -http=:6060 -play运行一个playground),还有 gofmt(自动格式化代码),还有gofix(自动根据go的版本更新你的调用函数)。
通常的覆盖测试的方法是构造二进制文件。比如 gcov 程序在程序的每个分支设置断点,一旦这个分支被执行,断点被清除,目标分支被标记为“已覆盖”。
这种方式很成功而且被广泛运用,但是它的问题在于难以实现,更重要的是,它是不可移植的。不同的架构,甚至不同的系统下都需要重新设置,因为它们的调试方法各不相同。
在Go的新的覆盖测试方法中使用了一种新的方式来避免动态调试。方法很简单:那就是在编译之前重写代码,然后编译运行,打印统计信息。重写代码非常简单因为 go 命令可以控制整个 “代码->测试->执行” 流程。
让我们来运行一个简单的例子,创建文件夹 test_cover,在其中创建文件:
// size.go
package size
func Size(a int) string {
    switch {
    case a < 0:
        return "negative"
    case a == 0:
        return "zero"
    case a < 10:
        return "small"
    case a < 100:
        return "big"
    case a < 1000:
        return "huge"
    }
    return "enormous"
}测试文件:
// size_test.go
package size
import "testing"
type Test struct {
    in  int
    out string
}
var tests = []Test{
    {-1, "negative"},
    {5, "small"},
}
func TestSize(t *testing.T) {
    for i, test := range tests {
        size := Size(test.in)
        if size != test.out {
            t.Errorf("#%d: Size(%d)=%s; want %s", i, test.in, size, test.out)
        }
    }
}PS:文件名须以"_test.go"结尾;方法名须以"Test"打头,并且形参为 (t *testing.T)
运行 go test -cover,得到测试结果:
$ go test -cover
PASS
coverage: 42.9% of statements
ok      test_cover      0.125s当我们运行 cover 工具的时候,源代码会在编译之前被重写,让我们来看一看重写后的Size函数:
func Size(a int) string {
    GoCover.Count[0] = 1
    switch {
    case a < 0:
        GoCover.Count[2] = 1
        return "negative"
    case a == 0:
        GoCover.Count[3] = 1
        return "zero"
    case a < 10:
        GoCover.Count[4] = 1
        return "small"
    case a < 100:
        GoCover.Count[5] = 1
        return "big"
    case a < 1000:
        GoCover.Count[6] = 1
        return "huge"
    }
    GoCover.Count[1] = 1
    return "enormous"
}每一个可执行的分支都被加入了一个赋值语句,当我们执行完之后,计数器开始工作并将覆盖结果返回给我们。
我们的测试结果的覆盖率很差,可以通过输出文件来看看原因:go test -coverprofile=coverage.out
coverprofile参数会自动设置cover参数,运行完之后,我们可以看到当前目录下存在 coverage.out 文件:
# coverage.out
mode: set
test_cover\size.go:3.25,4.12 1 1
test_cover\size.go:16.5,16.22 1 0
test_cover\size.go:5.16,6.26 1 1
test_cover\size.go:7.17,8.22 1 0
test_cover\size.go:9.17,10.23 1 1
test_cover\size.go:11.18,12.21 1 0
test_cover\size.go:13.19,14.22 1 0
我们可以分析该文件,得到每个function的覆盖率,使用命令:
$ go tool cover -func=coverage.out
test_cover\size.go:3:   Size            42.9%
total:                  (statements)    42.9%由于这个例子中只有一个函数,所以结果只有一个。我们可以用一个更清晰的HTML页面来显示结果:
go tool cover -html=coverage.out此时会自动打开一个HTML页面,

在此处可以非常清晰地看到哪些分支被执行了。
这种代码级别的覆盖测试工具还有其他作用。比如它不仅仅可以告诉你一条语句是否被执行了,还可以告诉你它执行了多少次。
go test 接受 -covermode参数,一共有三种设置:
可以按照上述方式重新进行测试,可以看到在html界面中不同覆盖率的语句用不同的颜色表示出来了。比如例子:

可以将鼠标移动到每一条语句上查看其执行次数。
计数的结果大概如下所示,左侧是执行次数:
2933    if !f.widPresent || f.wid == 0 {
2985        f.buf.Write(b)
2985        return
2985    }
  56    padding, left, right := f.computePadding(len(b))
  56    if left > 0 {
  37        f.writePadding(left, padding)
  37    }
  56    f.buf.Write(b)
  56    if right > 0 {
  13        f.writePadding(right, padding)
  13    }
如果观察上个例子可以发现,覆盖计数无法区分一条语句中的不同部分。
f() && g()
比如这样的情况,但是你必须认识到覆盖测试是一种不精确的方法。
顺带一提的是,gcov也存在这个问题......
在源文件目录下编写xxx_test.go文件,在其中编写TestXxx函数,然后执行覆盖测试。
相关函数汇总:
go test -cover                                       # 执行测试
go test -coverprofile=coverage.out --covermode=count    # 执行count模式的模糊测试并保存测试结果
go tool cover -func=coverage.out                       # 分析每个函数执行情况
go tool cover -html=coverage.out                       # 显示可视化结果标签:com out ase src alt 格式化 精确 idp intro
原文地址:https://www.cnblogs.com/Murakami/p/9240965.html