go 单元测试(一)

单元测试

Go 语言测试框架可以让我们很容易地进行单元测试,但是需要遵循下面的规则:

  1. 含有单元测试代码的 go 文件必须以 _test.go 结尾,Go 语言测试工具只认符合这个规则的文件。

  2. 单元测试的函数名必须以 Test 开头,是可导出的、公开的函数,测试函数的签名必须接收一个指向 testing.T 类型的指针,并且不能返回任何值。

    func TestFibonacci(t *testing.T) {
        // ...
    }
    
  3. 单元测试文件名 _test.go 前面的部分最好是被测试的函数所在的 go 文件的文件名,比如下面示例中单元测试文件叫 main_test.go,因为测试的 Fibonacci 函数在 main.go 文件里。 函数名最好是 Test + 要测试的函数名,比如例子中是 TestFibonacci,表示测试的是 Fibonacci 这个函数。

  • ch/fibonacci.go

    package main
    
    func Fibonacci(n int) int {
        if n < 0 {
            return 0
        }
    
        if n == 0 {
            return 0
        }
    
        if n == 1 {
            return 1
        }
    
        return Fibonacci(n-1) + Fibonacci(n-2)
    }
    
  • ch/fibonacci_test.go

    package main
    import "testing"
    
    func TestFibonacci(t *testing.T) {
        // 预先定义的一组斐波那契数列作为测试用例(表驱动)
        caseMap := map[int]int{
          0: 0,
          1: 1,
          2: 1,
          3: 2,
          4: 3,
          5: 5,
          6: 8,
          7: 13,
          8: 21,
        }
    
        for k, v := range caseMap {
            fib := Fibonacci(k)
            if v == fib {
                t.Logf("结果正确:n为%d,值为%d", k, fib)
            } else {
                t.Errorf("结果错误:期望%d,但是计算的值是%d", v, fib)
            }
        }
    }
    

    t.Error = Log + Fail
    t.Errorf = Logf + Fail
    t.Fatal = Log + FailNow
    t.Fatalf = Logf + FailNow

运行单元测试:

# go test -v .
  === RUN   TestFibonacci
    fibonacci_test.go:22: 结果正确:n为0,值为0
    fibonacci_test.go:22: 结果正确:n为2,值为1
    fibonacci_test.go:22: 结果正确:n为5,值为5
    fibonacci_test.go:22: 结果正确:n为6,值为8
    fibonacci_test.go:22: 结果正确:n为8,值为21
    fibonacci_test.go:22: 结果正确:n为9,值为34
    fibonacci_test.go:22: 结果正确:n为1,值为1
    fibonacci_test.go:22: 结果正确:n为3,值为2
    fibonacci_test.go:22: 结果正确:n为4,值为3
    fibonacci_test.go:22: 结果正确:n为7,值为13
  --- PASS: TestFibonacci (0.00s)
  PASS
  ok      ch18/main   0.002s

-v 参数会显示每个用例的测试结果。

断言

Go 语言官方库并没有提供断言的功能,官方认为这可能导致用户懒于思考良好的错误处理和汇报。但没有断言实在是不方便,所以我们可以使用第三方库 testify 来实现:

package main
import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestFibonacci(t *testing.T) {
    // 预先定义的一组斐波那契数列作为测试用例(表驱动)
    caseMap := map[int]int{
        0: 0,
        1: 1,
        2: 1,
        3: 2,
        4: 3,
        5: 5,
        6: 8,
        7: 13,
        8: 21,
    }

    for k, v := range caseMap {
        fib := Fibonacci(k)
        assert.Equal(t, fib, v)
    }
}

常用的 assert 有:

  • assert.Equal
  • assert.NotEqual
  • assert.Zero
  • assert.NotZero
  • assert.Nil
  • assert.NotNil
setup 和 teardown

如果在同一个测试文件中,每一个测试用例运行前后的逻辑是相同的,一般会写在 setupteardown 函数中。例如执行前需要实例化待测试的对象,如果这个对象比较复杂,很适合将这一部分逻辑提取出来;执行后,可能会做一些资源回收类的工作,例如关闭网络连接,释放文件等

package main

func setup() {
    fmt.Println("Before all tests")
}

func teardown() {
    fmt.Println("After all tests")
}

func Test1(t *testing.T) {
    fmt.Println("I'm test1")
}

func Test2(t *testing.T) {
    fmt.Println("I'm test2")
}

func TestMain(m *testing.M) {
    setup()
    code := m.Run()
    teardown()
    os.Exit(code)
}

注意:如果测试文件中包含函数 TestMain,那么生成的测试将调用 TestMain(m),而不是直接运行测试,并且一个包中只能有一个TestMain

子测试
表驱动

对于多个子测试的场景,更推荐表驱动的写法:

cases := []struct {
        Name           string
        A, B, Expected int
    }{
        {"pos", 2, 3, 6},
        {"neg", 2, -3, -6},
        {"zero", 2, 0, 0},
    }

    for _, c := range cases {
        t.Run(c.Name, func(t *testing.T) {
            if ans := Mul(c.A, c.B); ans != c.Expected {
                t.Fatalf("%d * %d expected %d, but %d got",
                    c.A, c.B, c.Expected, ans)
            }
        })
    }
运行项目下的所有测试用例

在项目根目录下执行go test ./...

指定运行部分测试

如果只想运行一部分测试,我们可以在命令时指定:

# go test -run TestFibonacci

并且支持部分通配符*,和部分正则表达式,例如^$

测试覆盖率以及发布报告

-cover参数可以查看覆盖率。
如果想要测试覆盖率的报告,那么首先:

go test test.go -coverprofile=covprofile

接着再使用工具解析上面的报告,将其转换为html结果网页

go tool cover -html=covprofile -o coverage.html
编译测试为可执行文件

有时,我们编写的测试文件,需要在不同的测试机器上运行,那么可以使用

go test -c 

将测试编译为可执行文件。

如何调试测试
  • 方法一
    # dlv test
    
  • 方法二
    使用 go test -c 将测试编译为可执行文件,然后再使用 dlv 调试。

你可能感兴趣的:(go 单元测试(一))