Go的单元测试
单元测试的作用
单元测试一般用来测试自己写的代码逻辑是否有问题,能不能按照自己的预期执行,便于自己在上线之前检验代码质量。
在我自己使用单元测试的过程中,我自己一般都是针对某个函数进行测试,判断这个函数是否能够达到预期效果。为了在Go中使用单元测试,我们需要在xxx_test.go
文件中导入testing
包,通过go test
命令实现自动执行如下形式的函数:
func TestFuncName(*testing.T)
需要注意的是:
FuncName
的函数名第一个字母不能小写
在上面的测试函数中,如果需要发送失败消息我们可以使用Error
或者Fail
和Fatal
这些方法,执行成功的话我们也可以使用Log
函数输出一些信息。
在编写单元测试的时候,我们需要创建一个以_test.go
结尾的文件,文件中包含了Test_FuncName
函数。并且这两个文件放在同一个package下。通过执行go test xxx.go
来进行单元测试。
单元测试示例
在这我将会在strings.go中写个方法Contains
,然后在strings_test.go文件中写一个测试函数TestContains
,并且我将这两个文件放在同一个package下,具体如下所示:
strings.go
func Contains(s, substr string) bool {
return strings.Contains(s, substr)
}
strings_test.go
func TestContains(t *testing.T) {
res := basic.Contains("res", "e")
if res == true {
t.Log("the result is OK")
} else {
t.Fatal("the result is wrong")
}
}
然后打开cmd窗口,输入命令go test -v .\strings_test.go,可以看到输出结果如下:
=== RUN TestContains
TestContains: strings_test.go:12: the result is OK
--- PASS: TestContains (0.00s)
PASS
ok command-line-arguments (cached)
Table-Driven Test(TDT)
上面的测试中只输入了一个case,并没有实现case覆盖,测试结果可能会不全。所以如果在一个测试中需要测试多个case,如果我们直接在代码中输入多个case,这将会很费时费力。但是不需要担心,我们可以使用table-driven的方式来实现测试函数。为了实现多个case测试的示例,我在原来的文件中分别实现一个Add和Test_Add函数,示例如下:
strings.go
func Add(a, b int) int {
return a + b
}
strings_test.go
func TestAdd(t *testing.T) {
// define table
var addtests = []struct {
a int
b int
res int // expected result
}{
{1,1,2},
{2,3,5},
{3,4,7},
{5,6,11},
{6,5,11},
{1,22,23},
}
for _, sum := range addtests {
actual := basic.Add(sum.a, sum.b)
if actual != sum.res {
t.Errorf("Add(%d + %d ) = %d, expected result is %d", sum.a, sum.b, actual ,sum.res)
}
}
}
输入命令go test -v .\strings_test.go得到如下结果
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok command-line-arguments 0.222s
testing的结构体
- B : 压力测试
- BenchmarkResult : 压力测试结果
- Cover : 代码覆盖率相关结构体
- CoverBlock : 代码覆盖率相关结构体
- InternalBenchmark : 内部使用的结构体
- InternalExample : 内部使用的结构体
- InternalTest : 内部使用的结构体
- M : main 测试使用的结构体
- PB : Parallel benchmarks 并行测试使用的结构体
- T : 普通测试用例
- TB : 测试用例的接口
testing的通用方法
- 碰到断言错误,会判断这个测试用例失败,可能会使用到
Fail : case失败,继续后面的测试用例
FaileNow: case失败,终止测试
- 碰到断言错误时希望跳过这个错误,但是不希望标识case失败,使用
SkipNow : case跳过,终止测试
- 只希望在一个地方打印信息,使用
Log: 输出信息
Logf: 输出格式化的信息
- 希望跳过这个case并且打印信息
Skip: = Log + SkipNow
Skipf: = Logf + SkipNow
- case失败的时候打印出信息,并且中断测试用例
Fatal: = Log + FailNow
Fatalf: = Logf + FailNow
- case失败的时候打印出信息,并且希望测试继续
Error: = Log + Fail
Errorf: = Logf + Fail