Golang测试方法(一)

本篇文章内容来源于Golang核心开发组成员Andrew Gerrand在Google I/O 2014的一次主题分享“Testing Techniques”,即介绍使用Golang开发 时会使用到的测试技术(主要针对单元测试),包括基本技术、高级技术(并发测试、mock/fake、竞争条件测试、并发测试、内/外部测 试、vet工具等)等,感觉总结的很全面,这里整理记录下来,希望能给大家带来帮助。原Slide访问需要自己搭梯子。另外这里也要吐槽一 下:Golang官方站的slide都是以一种特有的golang artical的格式放出的(用这个工具http://go-talks.appspot.com/可以在线观看),没法像pdf那样下载,在国内使用和传播极其不便。

一、基础测试技术

  • 1、测试Go代码

Go语言内置测试框架。

内置的测试框架通过testing包以及go test命令来提供测试功能。

下面是一个完整的测试strings.Index函数的完整测试文件:

//strings_test.go (这里样例代码放入strings_test.go文件中)
package strings_test

import (
    "strings"
    "testing"
)

func TestIndex(t *testing.T) {
    const s, sep, want = "chicken", "ken", 4
    got := strings.Index(s, sep)
    if got != want {
        t.Errorf("Index(%q,%q) = %v; want %v", s, sep, got, want)//注意原slide中的got和want写反了
    }
}

如下:

$go test -v strings_test.go
=== RUN TestIndex
— PASS: TestIndex (0.00 seconds)
PASS
ok      command-line-arguments    0.007s

go test的-v选项是表示输出详细的执行信息。

将代码中的want常量值修改为3,我们制造一个无法通过的测试:

$go test -v strings_test.go
=== RUN TestIndex
— FAIL: TestIndex (0.00 seconds)
    strings_test.go:12: Index("chicken","ken") = 4; want 3
FAIL
exit status 1
FAIL    command-line-arguments    0.008s
  • 2、表驱动测试

Golang的struct字面值(struct literals)语法让我们可以轻松写出表驱动测试。

package strings_test

import (
        "strings"
        "testing"
)

func TestIndex(t *testing.T) {
        var tests = []struct {
                s   string
                sep string
                out int
        }{
                {"", "", 0},
                {"", "a", -1},
                {"fo", "foo", -1},
                {"foo", "foo", 0},
                {"oofofoofooo", "f", 2},
                // etc
        }
        for _, test := range tests {
                actual := strings.Index(test.s, test.sep)
                if actual != test.out {
                        t.Errorf("Index(%q,%q) = %v; want %v",
                             test.s, test.sep, actual, test.out)
                }
        }
}

结果如下:

$go test -v strings_test.go
=== RUN TestIndex
— PASS: TestIndex (0.00 seconds)
PASS
ok      command-line-arguments    0.007s
  • 3、T结构

*testing.T参数用于错误报告:

t.Errorf("got bar = %v, want %v", got, want)
t.Fatalf("Frobnicate(%v) returned error: %v", arg, err)
t.Logf("iteration %v", i)

也可以用于enable并行测试(parallet test):
t.Parallel()

控制一个测试是否运行:

if runtime.GOARCH == "arm" {
    t.Skip("this doesn't work on ARM")
}
  • 4、运行测试

我们用go test命令来运行特定包的测试。

默认执行当前路径下包的测试代码。

$ go test
PASS

$ go test -v
=== RUN TestIndex
— PASS: TestIndex (0.00 seconds)
PASS

要运行工程下的所有测试,我们执行如下命令:

$ go test github.com/nf/…

标准库的测试:
$ go test std

注:假设strings_test.go的当前目录为testgo,在testgo目录下执行go test都是OK的。但如果我们切换到testgo的上一级目录执行go test,我们会得到什么结果呢?

$go test testgo
can't load package: package testgo: cannot find package "testgo" in any of:
    /usr/local/go/src/pkg/testgo (from $GOROOT)
    /Users/tony/Test/GoToolsProjects/src/testgo (from $GOPATH)

提示找不到testgo这个包,go test后面接着的应该是一个包名,go test会在GOROOT和GOPATH下查找这个包并执行包的测试。

  • 5、测试覆盖率

go tool命令可以报告测试覆盖率统计。

我们在testgo下执行go test -cover,结果如下:

go build _/Users/tony/Test/Go/testgo: no buildable Go source files in /Users/tony/Test/Go/testgo
FAIL    _/Users/tony/Test/Go/testgo [build failed]

显然通过cover参数选项计算测试覆盖率不仅需要测试代码,还要有被测对象(一般是函数)的源码文件。

我们将目录切换到$GOROOT/src/pkg/strings下,执行go test -cover:

$go test -v -cover
=== RUN TestReader
— PASS: TestReader (0.00 seconds)
… …
=== RUN: ExampleTrimPrefix
— PASS: ExampleTrimPrefix (1.75us)
PASS
coverage: 96.9% of statements
ok      strings    0.612s

go test可以生成覆盖率的profile文件,这个文件可以被go tool cover工具解析。

在$GOROOT/src/pkg/strings下面执行:

$ go test -coverprofile=cover.out

会再当前目录下生成cover.out文件。

查看cover.out文件,有两种方法:

a) cover -func=cover.out

$sudo go tool cover -func=cover.out
strings/reader.go:24:    Len                66.7%
strings/reader.go:31:    Read                100.0%
strings/reader.go:44:    ReadAt                100.0%
strings/reader.go:59:    ReadByte            100.0%
strings/reader.go:69:    UnreadByte            100.0%
… …
strings/strings.go:638:    Replace                100.0%
strings/strings.go:674:    EqualFold            100.0%
total:            (statements)            96.9%

b) 可视化查看

执行go tool cover -html=cover.out命令,会在/tmp目录下生成目录coverxxxxxxx,比如/tmp/cover404256298。目录下有一个 coverage.html文件。用浏览器打开coverage.html,即可以可视化的查看代码的测试覆盖情况。

关于go tool的cover命令,我的go version go1.3 darwin/amd64默认并不自带,需要通过go get下载。

$sudo GOPATH=/Users/tony/Test/GoToolsProjects go get code.google.com/p/go.tools/cmd/cover

下载后,cover安装在$GOROOT/pkg/tool/darwin_amd64下面。