一、单元测试用例编写规则
- 测试文件的名称必须以**_test.go
- 测试函数必须和被测试的方法在同一个包下
- 测试函数的传参必须是*testing.T类型
- 测试函数的函数名称必须以Test****开头,且Test后的第一个字母为除不可为小写字母外的任何字符串,如大写字母、下划线_、数字
go build 不会编译测试文件
二、testing.T类型中的常用函数
- 处理失败事件的方法
- 测试失败,打印出失败信息后,测试继续
- func (c *T) Error(args ...interface{})
- func (c *T) Errorf(format string, args ...interface{})
- 测试失败,不输出打印信息,测试继续
- func (c *T) Fail()
- 测试失败,不输出打印信息,测试中断
- func (c *T) FailNow()
- 测试失败,中断测试,打印出信息
- func (c *T) Fatal(args ...interface{})
- func (c *T) Fatalf(format string, args ...interface{})
- 测试失败,打印出失败信息后,测试继续
- 处理日志
- 输出日志,对于测试流程没有影响,但是需要留意,在单元测试中,测试成功时,它们打印的信息不会输出,可以通过加上 -v 选项,输出这些信息。测试失败时,如果执行的流程中存在该方法,会进行输出。
- func (c *T) Log(args ...interface{})
- func (c *T) Logf(format string, args ...interface{}
- 输出日志,对于测试流程没有影响,但是需要留意,在单元测试中,测试成功时,它们打印的信息不会输出,可以通过加上 -v 选项,输出这些信息。测试失败时,如果执行的流程中存在该方法,会进行输出。
- 跳出测试
- 跳过测试,测试中断
- func (c *T) SkipNow()
- 跳过测试,又输出传入的参数,功能上相当于t.Log和t.SkipNow()的集合
- func (c *T) Skip(args ...interface{})
- func (c *T) Skipf(format string, args ...interface{})
- 跳过测试,测试中断
三、单元测试用例
创建ex_test.go文件,输入以下内容:
package UnitTest
import "testing"
func plus(a, b int) int {
return a + b
}
func Test_Plus(t *testing.T) {
var obj = struct {
val1 int
val2 int
sum int
}{
2, 3, 9,
}
t.Log("start.....")
if obj.val1+obj.val2 == obj.sum {
t.Logf("execute success:%d plus %d equals %d", obj.val1, obj.val2, obj.sum)
} else {
t.Errorf("execute failed: %d plus %d is not equal to %d ", obj.val1, obj.val2, obj.sum)
}
t.Skip("execute done!")
t.Skipped()
}
通过命令 go test ex_test.go执行代码,输出:
ok command-line-arguments 0.007s
执行命令go test -v ex_test.go ,输出:
=== RUN Test_Plus
--- SKIP: Test_Plus (0.00s)
ex_test.go:17: start.....
ex_test.go:19: execute success:2 plus 3 equals 5
ex_test.go:24: execute done!
PASS
ok command-line-arguments 0.007s
修改测试用例为:
var obj = struct {
val1 int
val2 int
sum int
}{
2, 3, 9,
}
执行 go test ex_test.go命令输出:
--- FAIL: Test_Plus (0.00s)
ex_test.go:17: start.....
ex_test.go:21: execute failed: 2 plus 3 is not equal to 9
ex_test.go:24: execute done!
FAIL
FAIL command-line-arguments 0.007s
基于以上测试发现,方法t.Skip() 、t.Skipf()具有 与t.Log()、t.Logf()一样的特征: 在单元测试中,测试成功时,它们打印的信息不会输出,需要通过加上 -v 选项,输出这些信息。测试失败时,如果执行的流程中存在该方法,会进行输出。
四、go test 常用命令参数
go语言通过go test 命令执行参数,
-
-v 输出测试的详细信息
-
-run 指定测试函数 例如:-run "TestA" 、-run="TestA" ,在函数名称中包含TestA的测试函数都会被执行,TestA可以包含在名称中的最前边,中间或结尾
-
-timeout 本次执行所用时间不可超时,否则会执行panic,默认是10s 例如:-timeout 1m30s
-
-count 设置测试执行的次数,默认值为1
-
-args 命令行参数
-
-parallel 多个测试函数并行执行,默认值为GOMAXPROCS 例如:-parallel 2
GOMAXPROCS一般指cpu核数
-
branch
测试特定文件或方法
-
测试特定文件 : 需要在命令行输入被测试的原文件
go test XXX_test.go XXX.go
-
测试特定方法
go test -v -test.run TestRefreshAccessToken
go test -v file_test.go -test.run TestFunc
五、Parallel
在同一个测试文件中,如果测试方法中不包含Parallel()方法,且测试文件中同时包含多个测试函数,那么测试函数按照顺序输出。
在parallel_test.go测试文件中输入以下代码,
package UnitTest
import (
"testing"
"time"
)
func Test_A(t *testing.T) {
time.Sleep(1 * time.Second)
t.Log("sleep 1 second .....")
}
func Test_B(t *testing.T) {
t.Log("test b ......")
}
执行命令 go test -v parallel_test.go,输出:
=== RUN Test_A
--- PASS: Test_A (1.00s)
parallel_test.go:10: sleep 1 second .....
=== RUN Test_B
--- PASS: Test_B (0.00s)
parallel_test.go:15: test b ......
PASS
ok command-line-arguments 1.010s
在上面的测试函数中添加方法t.Parallel():
package UnitTest
import (
"testing"
"time"
)
func Test_A(t *testing.T) {
t.Parallel()
time.Sleep(1 * time.Second)
t.Log("sleep 1 second .....")
}
func Test_B(t *testing.T) {
t.Parallel()
t.Log("test b ......")
}
执行命令 go test -v parallel_test.go,输出:
=== RUN Test_A
=== PAUSE Test_A
=== RUN Test_B
=== PAUSE Test_B
=== CONT Test_A
=== CONT Test_B
--- PASS: Test_B (0.00s)
parallel_test.go:17: test b ......
--- PASS: Test_A (1.00s)
parallel_test.go:11: sleep 1 second .....
PASS
ok command-line-arguments 1.012s
基于以上测试,Parallel()方法用于并行执行包含Parallel()方法的测试函数
六、子测试
从Go1.7开始,
在 run_test.go 文件中输入以下代码:
package UnitTest
import "testing"
func Test_A1(t *testing.T) {
t.Run("TestA", func(t *testing.T) {
t.Log("Test A")
})
t.Run("TestB", func(t *testing.T) {
t.Log("Test B")
})
t.Run("TestAB", func(t *testing.T) {
t.Log("Test AB")
})
}
func Test_B1(t *testing.T) {
t.Run("TestA", func(t *testing.T) {
t.Log("Test A")
})
t.Run("TestB", func(t *testing.T) {
t.Log("Test B")
})
t.Run("TestAB", func(t *testing.T) {
t.Log("Test AB")
})
}
func Test_C1(t *testing.T) {
t.Run("TestGroup", func(t *testing.T) {
t.Run("TestGroup_A", func(t *testing.T) {
t.Log("TestGroup A")
})
t.Run("TestGroup_B", func(t *testing.T) {
t.Log("TestGroup AB")
})
})
}
-
go test -v -run '' run_test.go 执行文件中的所以测试函数,和go test -v run_test.go 结果一样
-
go test -v -run Test_A/TestA run_test.go 执行测试函数中顶层函数包含Test_A的函数里面名字包含TestA的子测试函数
=== RUN Test_A1 === RUN Test_A1/TestA === RUN Test_A1/TestAB --- PASS: Test_A1 (0.00s) --- PASS: Test_A1/TestA (0.00s) run_test.go:7: Test A --- PASS: Test_A1/TestAB (0.00s) run_test.go:13: Test AB PASS ok command-line-arguments 0.007s -
go test -v -run /Group/Group_A run_test.go 子测试的函数可以是多层包含,该名称查找所有顶层测试函数中子测试名称包含Group的函数,再查找该子测试下的子测试名称包含Group_A的测试名称进行测试
=== RUN Test_A1 --- PASS: Test_A1 (0.00s) === RUN Test_B1 --- PASS: Test_B1 (0.00s) === RUN Test_C1 === RUN Test_C1/TestGroup === RUN Test_C1/TestGroup/TestGroup_A --- PASS: Test_C1 (0.00s) --- PASS: Test_C1/TestGroup (0.00s) --- PASS: Test_C1/TestGroup/TestGroup_A (0.00s) run_test.go:32: TestGroup A PASS ok command-line-arguments 0.009s -
go test -v -run test_/Group/Group_A run_test.go
执行结果:testing: warning: no tests to run PASS ok command-line-arguments 0.007s [no tests to run]-run 的名称匹配原则是根据 正则表达式,区分字母的大小写
根据上面的测试结果可以发现,执行测试命令时,go test 会遍历执行文件下所有的测试方法,找到方法名称和 -run 后面参数匹配的方法进行执行。
七、TestMain
八、例代码 Example
1.例代码 编写规则
- 例代码文件名称以 _test.go 结尾
- 例代码的方法必须以Example开头,且Example后的第一个字母为除不可为小写字母外的任何字符串,如大写字母、下划线_、数字
2.例子
在e_test.go文件中输入以下代码:
package TestM
import "fmt"
func add(a, b int32) int32 {
return a + b
}
func Example_Add() {
fmt.Println(add(2, 3))
fmt.Println(add(9, 3))
// Output:
// 5
// 12
}
文件中必须包含Output注释,fmt.Println(add(2, 3))和fmt.Println(add(9, 3)) 的执行结果会和Output后的信息进行匹配,如果相同,就执行成功。
执行:go test e_test.go输出:
ok command-line-arguments 0.007s
如果如果将Output部分的值修改,则
// Output:
// 5
// 122
执行go test e_test.go,输出:
--- FAIL: Example_Add (0.00s)
got:
5
12
want:
5
122
FAIL
FAIL command-line-arguments 0.007s
两个fmt.Println的输出结果分别与output后的值进行匹配,必须全部匹配相同才为执行成功,或者均失败报错。
对for循环中输出进行匹配,用UnOrdered Output: 例如:
func Example_Map() {
var map_test map[int]string = make(map[int]string)
map_test[1] = "first"
map_test[2] = "second"
map_test[3] = "three"
map_test[4] = "four"
for _, v := range map_test {
fmt.Println(v)
}
// UnOrdered Output:
// first
// second
// three
// four
}
func (c *T) Helper()
func (c *T) Name() string
func (c *T) Failed() bool
func (c *T) Skipped() bool
-run
参数-run对应一个正则表达式,只有测试函数名被它正确匹配的测试函数才会被go test测试命令运行: $ go test -v -run="TestTwo"