golang提供了极为简洁的编写单元测试的方式,只需几行代码,即可轻松创建出一个测试用例,并且可以直接运行。

1.testing单元测试

testinggo test
func TestXXX(t *testing.T)
*testing.T

下面是一个简单的待测试待函数(funcs1.go):

package unit_1

//编写一个简单的方法
func Sum(arr []int) int {
	sum := 0
	for v := range arr { //故意写错的..
		sum += v
	}
	return sum
}

另外创建一个文件(funcs1_test.go)包含以下测试方法:

package unit_1

import (
	"testing"
)

func TestSum(t *testing.T) {
	testArr := []int{1, 3, 5, 7}
	expected := 16

	actual := Sum(testArr)
	if actual != expected {
		t.Errorf("sum result error.input:%+v expected:%d actual:%d", testArr, expected, actual)
	}
}

使用如下命令执行测试操作:

 go test . 

结果如下:

--- FAIL: TestSum (0.00s)
    funcs1_test.go:13: sum result error.input:[1 3 5 7] expected:16 actual:6
FAIL
FAIL    testAnything/unit_1     0.006s

发现测试失败,说明函数存在bug。
原因是for range 单参数获取的是索引值,而我们需要的是每个数组元素的值,改正后再次运行可以发现:

ok      testAnything/unit_1     0.005s

这次运行成功了!!

2.func TestMain(m *testing.M)

若被测试的方法依赖某些环境,需要首先初始化环境,测试结束后,又需要做清理工作(例如关闭连接)等等可以用TestMain()。如下:

func TestMain(m *testing.M) {
	fmt.Println("init anything env....")
	m.Run()
	fmt.Println("handle close etc..")
}
go test -v
init anything env....
=== RUN   TestSum
--- PASS: TestSum (0.00s)
PASS
handle close etc..
ok      testAnything/unit_1     0.008s

3.介绍下go test的几个常用的参数:

-bench regexp-cover-run regexp-v

4.使用goland简化上述过程

使用goland可以快速构建测试用例,并且可以替代go test命令执行对应测试方法(本质其实还是go test,只不过交给了goland隐式的调用了)
还是以上面的sum方法为例:
Step.1 在需要测试的方法名出右键-> Generate…
Step1
Step.2 选择“Test for Function”
Step2
【注】:可能会出现以下提示,选择安装即可
tips
之后会生成如下的代码:

func TestSum(t *testing.T) {
	type args struct {
		arr []int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Sum(tt.args.arr); got != tt.want {
				t.Errorf("Sum() = %v, want %v", got, tt.want)
			}
		})
	}
}
nameargswant
func TestSum(t *testing.T) {
	type args struct {
		arr []int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		// TODO: Add test cases.
		{
			name: "case1",
			args: args{arr: []int{1, 2, 3, 4, 5, 6, 7}},
			want: 28,
		},
		{
			name: "case2",
			args: args{arr: []int{1, 2, 3, 4, 5, 6}},
			want: 22, //故意写错的,模拟测试失败
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Sum(tt.args.arr); got != tt.want {
				t.Errorf("Sum() = %v, want %v", got, tt.want)
			}
		})
	}
}

测试结果如下:

init anything env....
=== RUN   TestSum
--- FAIL: TestSum (0.00s)
=== RUN   TestSum/case1
    --- PASS: TestSum/case1 (0.00s)
=== RUN   TestSum/case2
    --- FAIL: TestSum/case2 (0.00s)
        funcs1_test.go:49: Sum() = 21, want 22
FAIL
handle close etc..

Process finished with exit code 0
|–进阶---->

1、单元测试会用的其他的方法:

f
方法名作用
Log、Logf输出信息
Fail、Failf测试失败,测试终止,相当于 Logf + FailNow
FailNow测试失败,并终止测试
Error、Errorf标示出现错误,并会继续测试,相当于 Log + Fail
Skip、Skipf相当于 Log[f] + SkipNow
SkipNow跳过测试,测试中断
runtime.Goexit()

2、子测试

如果需要在一个测试方法中运行另一个测试方法,可以使用子测试:

func TestSum(t *testing.T) {
	type args struct {
		arr []int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		// TODO: Add test cases.
		{
			name: "case1",
			args: args{arr: []int{1, 2, 3, 4, 5, 6, 7}},
			want: 28,
		},
		{
			name: "case2",
			args: args{arr: []int{1, 2, 3, 4, 5, 6}},
			want: 21, //故意写错的,模拟测试失败
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Sum(tt.args.arr); got != tt.want {
				t.Errorf("Sum() = %v, want %v", got, tt.want)
			}
		})
	}

	t.Run("sub Testing", TestSum2)
}

func TestSum2(t *testing.T) {
	fmt.Println("do something...")
}

全部方法测试通过才算PASS

init anything env....
=== RUN   TestSum
--- PASS: TestSum (0.00s)
=== RUN   TestSum/case1
    --- PASS: TestSum/case1 (0.00s)
=== RUN   TestSum/case2
    --- PASS: TestSum/case2 (0.00s)
=== RUN   TestSum/sub_Testing
do something...
    --- PASS: TestSum/sub_Testing (0.00s)
PASS
handle close etc..

Pass!