Go语言编写单元测试用例

Go单元测试示例

example/
   |--division.go
   |--division_test.go

为什么被测试文件和测试文件通常放到同一个文件夹下以及同一个声明包里

通常情况下,我们把被测试的文件与另外写的测试文件放到同一个声明包里面,称为包内测试

当然也可以把测试函数的文件放到独立的一个包里面,称为包外测试

不过,包外测试源码文件存在一个弊端,那就是在它们的测试函数中无法测试被测源码文件中的包级私有的程序实体,比如包级私有的变量函数结构体类型。这是因为这两者的所属代码包是不相同的。所以,一般很少会编写包外测试源码文件。

单元测试规则

  • 测试文件名必须是_test.go结尾的,这样在执行go test的时候才会执行到相应的代码
  • 你必须import testing这个包
  • 所有的测试用例函数必须是Test开头
  • 测试用例会按照源代码中写的顺序依次执行
  • 测试函数TestXxx(t *testing.T)只有一个参数 t, 可以用 t 记录错误或者是测试状态
  • 测试函数的格式:func TestXxx (t *testing.T),Xxx部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv是错误的函数名。
  • 函数中通过调用testing.T的Error, Errorf, FailNow, Fatal, FatalIf方法,说明测试不通过,调用Log方法用来记录测试的信息。

单元测试写法

两种写法:自己手写、借助工具生成

方法1:自己手写

等待被测试的文件 division.go 如下,需要在同一个包里写一个测试文件命名为mydivision_test.go

 1 package math
 2 
 3 import (
 4     "errors"
 5 )
 6 
 7 func Division(a, b float64) (float64, error) {
 8     if b == 0 {
 9         return 0, errors.New("除数不能为0")
10     }
11 
12     return a / b, nil
13 }

直接自己撸测试函数,这样比较灵活想怎么测就怎么测

mydivision_test.go

 1 package math
 2 
 3 import (
 4     "testing"
 5 )
 6 
 7 //用例1
 8 func Test_Division_1(t *testing.T) {
 9     if i, e := Division(6, 2); i != 3 || e != nil { 
10         t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
11     } else {
12         t.Log("第一个测试通过了") //记录一些你期望记录的信息
13     }
14 }
15 //用例2
16 func Test_Division_2(t *testing.T) {
17     t.Error("执行t.Error分支就会提示测试失败")
18 }

运行命令go test -v .结果如下

$ go test  -v .
=== RUN   Test_Division_1
--- PASS: Test_Division_1 (0.00s)        
        mydivision_test.go:11: 第一个测试通过了
=== RUN   Test_Division_2
--- FAIL: Test_Division_2 (0.00s)
        mydivision_test.go:16: 执行t.Error分支就会提示测试失败
FAIL
exit status 1
FAIL    mytest/goTest   0.001s

方法二:借助工具生成

步骤:

  1. 先安装gotests工具包: go get -v github.com/cweill/gotests/...
  2. 运行命令gotests -all -w .为当前目录所有文件生成对应测试文件

结果为division.go生成了division_test.go文件如下,生成文件的默认前缀为对应的原文件名

division_test.go,然后留出一片“// TODO: Add test cases.”区域来填用例

 1 package math
 2 
 3 import "testing"
 4 
 5 func TestDivision(t *testing.T) {
 6     type args struct {
 7         a float64
 8         b float64
 9     }
10     tests := []struct {
11         name    string  //用例名字
12         args    args    //传给被测函数的参数
13         want    float64 //预期返回结果
14         wantErr bool    //用bool方便判断是否返回error,如果类型改为error反而不好判断
15     }{
16         // TODO: Add test cases.
17         {"case 0", args{6, 2}, 3, false},
18         {"case 1", args{6, 0}, 0, true},//注意第二个用例是会返回error的,因为除数不能为0,所以此处wantErr为true
19     }
20     for _, tt := range tests {
21         t.Run(tt.name, func(t *testing.T) {
22             got, err := Division(tt.args.a, tt.args.b)
23             if (err != nil) != tt.wantErr {
24                 t.Errorf("Division() error = %v, wantErr %v", err, tt.wantErr)
25                 return
26             }
27             if got != tt.want {
28                 t.Errorf("Division() = %v, want %v", got, tt.want)
29             }
30         })
31     }
32 }

运行结果

两个用例都通过了测试

$ go test -v .
=== RUN TestDivision
=== RUN TestDivision/case_0
=== RUN TestDivision/case_1
--- PASS: TestDivision (0.00s)
--- PASS: TestDivision/case_0 (0.00s)
--- PASS: TestDivision/case_1 (0.00s)
PASS
ok mytest/goTest 0.001s