go单元测试

单元测试用例函数以 Test 开头,例如 TestXxx 或 Test_xxx ( Xxx 部分为任意字母数字组合,首字母大写)。函数参数必须是 *testing.T,可以使用该类型来记录错误或测试状态。我们可以调用 testing.T 的 Error 、Errorf 、FailNow 、Fatal 、FatalIf 方法,来说明测试不通过;调用 Log 、Logf 方法来记录测试信息。函数列表和相关描述如下表所示:

image-20220401150507544

单元测试使用例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

func TestAbs(t *testing.T) {
    got := Abs(-1)
    if got != 1 {
        t.Errorf("Abs(-1) = %f; want 1", got)
    }
}

func TestMax(t *testing.T) {
    got := Max(1, 2)
    if got != 2 {
        t.Errorf("Max(1, 2) = %f; want 2", got)
    }
}

执行go test命令来执行如上单元测试用例:

1
go test

go test命令自动搜集所有的测试文件,也就是格式为*_test.go的文件,从中提取全部测试函数并执行。

go test 命令参数

参数 解释
-v 显示所有测试函数的运行细节:
-run <regexp> 指定要执行的测试函数
-count $N$ 指定执行测试函数的次数
1
2
3
4
5
6
7
8

$ go test -v -run='TestA.*' -count=2
=== RUN   TestAbs
--- PASS: TestAbs (0.00s)
=== RUN   TestAbs
--- PASS: TestAbs (0.00s)
PASS
ok      github.com/marmotedu/gopractise-demo/31/test    0.002s
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

func TestAbs_3(t *testing.T) {
    tests := []struct {
        x    float64
        want float64
    }{
        {-0.3, 0.3},
        {-2, 2},
        {-3.1, 3.1},
        {5, 5},
    }

    for _, tt := range tests {
        got := Abs(tt.x)
        assert.Equal(t, got, tt.want)
    }
}

使用 assert对结果断言

使用 assert有一个好处

  • 友好的输出结果,易于阅读。
  • 为少了if got := Xxx(); got != tt.wang {}的判断,代码变得更加简洁。
  • 可以针对每次断言,添加额外的消息说明,例如assert.Equal(t, got, tt.want, “Abs test”)。
  • assert 包还提供了很多其他函数,供开发者进行结果对比,例如Zero、NotZero、Equal、NotEqual、Less、True、Nil、NotNil等。如果想了解更多函数,你可以参考go doc github.com/stretchr/testify/assert。

自动生成单元测试框架

既然测试用例可以抽象成一些模型,那么我们就可以基于这些模型来自动生成测试代码。Go 社区中有一些优秀的工具可以自动生成测试代码,我推荐你使用gotests工具

gotests项目链接

1
2
3
4
5
6

$ go get -u github.com/cweill/gotests/...

# 生成单元测试

$ gotests -all -w .

性能测试 benchmark

上面,我讲了用来测试代码的功能是否正常的单元测试,接下来我们来看下性能测试,它是用来测试代码的性能是否满足需求的。性能测试的用例函数必须以Benchmark开头,例如BenchmarkXxx或Benchmark_Xxx( Xxx 部分为任意字母数字组合,首字母大写)。

1
2
3
4
5
6

func BenchmarkRandInt(b *testing.B) {
    for i := 0; i < b.N; i++ {
        RandInt()
    }
}

go test命令默认不会执行性能测试函数,需要通过指定参数-bench 来运行性能测试函数。-bench后可以跟正则表达式,选择需要执行的性能测试函数,例如go test -bench=".*"表示执行所有的压力测试函数。执行go test -bench=".*" 后输出如下:

1
2
3
4
5
6
7
8

$ go test -bench=".*"
goos: linux
goarch: amd64
pkg: github.com/marmotedu/gopractise-demo/31/test
BenchmarkRandInt-4      97384827                12.4 ns/op
PASS
ok      github.com/marmotedu/gopractise-demo/31/test    1.223s

benchmark参数

参数 解释
benchmem 输出内存分配统计
bench 指定性能测试【正则表达式】
benchtime 指定测试时间和循环执行次数
cpu 指定 GOMAXPROCS
timeout 测试函数执行超时时间
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
go test -bench=".*" -timeout=10s
go test -bench=".*" -benchtime=10s # 指定测试时间


$ go test -bench=".*" -benchmem
goos: linux
goarch: amd64
pkg: github.com/marmotedu/gopractise-demo/31/test
BenchmarkRandInt-4      96776823                12.8 ns/op             0 B/op          0 allocs/op
PASS
ok      github.com/marmotedu/gopractise-demo/31/test    1.255s

Convey框架

博客文章

github地址

go monkey patch框架

项目地址

参考视频

猴子补丁(Monkey Patch)可以在程序运行期间动态修改函数行为。也有人把这种技术叫「打桩」。这种技术在Python或者Ruby这样的动态语言中比较常见。但这并不意味着静态语言不能实现类似的效果。

框架原理介绍

  • 修改编译出来的文件的机器码,将函数跳转地址进行修改,实现动态修改程序的效果,类似切面编程等功能

解决的问题

  • 如何获取 函数 body地址?
  • 如何修改函数body 指令?
  • 如何构造函数跳转指令

go里面的 uintptr 类型c语言的 void *指针,可以指向任意类型