第10课:Golang测试技术

谁也不能保证自己写的代码没有bug,我们有哪些方式可以测试我们的go程序呢?容我细细道来

1.直接用liteIDE编译运行,查看错误信息

2.Go语言有内置测试框架,可以直接使用
内置的测试框架通过testing包以及go test命令来提供测试功能
例子:
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

3.测试覆盖率
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参数选项计算测试覆盖率不仅需要测试代码,还要有被测对象(一般是函数)的源码文件

4.高级测试技术
net/http/httptest包提供了许多帮助函数,用于测试那些发送或处理Http请求的代码。

httptest.Server在本地回环网口的一个系统选择的端口上listen。它常用于端到端的HTTP测试。

outyet是一个web服务,用于宣告某个特定Go版本是否已经打标签发布了。其获取方法:
go get github.com/golang/example/outyet

5.竞争检测(race detection)

当两个goroutine并发访问同一个变量,且至少一个goroutine对变量进行写操作时,就会发生数据竞争(data race)。

为了协助诊断这种bug,Go提供了一个内置的数据竞争检测工具。

通过传入-race选项,go tool就可以启动竞争检测。

$ go test -race mypkg    // to test the package
$ go run -race mysrc.go  // to run the source file
$ go build -race mycmd   // to build the command
$ go install -race mypkg // to install the package

注:一个数据竞争检测的例子

例子代码:

//testrace.go

package main

import "fmt"
import "time"

func main() {
        var i int = 0
        go func() {
                for {
                        i++
                        fmt.Println("subroutine: i = ", i)
                        time.Sleep(1 * time.Second)
                }
        }()

        for {
                i++
                fmt.Println("mainroutine: i = ", i)
                time.Sleep(1 * time.Second)
        }
}

$go run -race testrace.go
mainroutine: i =  1
==================
WARNING: DATA RACE
Read by goroutine 5:
  main.func·001()
      /Users/tony/Test/Go/testrace.go:10 +0×49

Previous write by main goroutine:
  main.main()
      /Users/tony/Test/Go/testrace.go:17 +0xd5

Goroutine 5 (running) created at:
  main.main()
      /Users/tony/Test/Go/testrace.go:14 +0xaf
==================
subroutine: i =  2
mainroutine: i =  3
subroutine: i =  4
mainroutine: i =  5
subroutine: i =  6
mainroutine: i =  7
subroutine: i =  8

6.测试并发(testing with concurrency)
当测试并发代码时,总会有一种使用sleep的冲动。大多时间里,使用sleep既简单又有效。
但我们可以使用Go的并发原语让那些奇怪不靠谱的sleep驱动的测试更加值得信赖。

7.子进程测试
有些时候,你需要测试的是一个进程的行为,而不仅仅是一个函数。例如:

func Crasher() {
    fmt.Println("Going down in flames!")
    os.Exit(1)
}

为了测试上面的代码,我们将测试程序本身作为一个子进程进行测试:

func TestCrasher(t *testing.T) {
    if os.Getenv("BE_CRASHER") == "1" {
        Crasher()
        return
    }
    cmd := exec.Command(os.Args[0], "-test.run=TestCrasher")
    cmd.Env = append(os.Environ(), "BE_CRASHER=1")
    err := cmd.Run()
    if e, ok := err.(*exec.ExitError); ok && !e.Success() {
        return
    }
    t.Fatalf("process ran with err %v, want exit status 1", err)
}