前言
- 基本上每一种高级计算机语言都有自己的代码逻辑测试的方式,也就是单元测试,其对于高级计算机语言的重要程度,大家在学习其他计算机语言的时候都有所了解,golang的单元测试,自身提供的testing package能够满足基本的测试需求,当然还有丰富API的第三方开源库,比如 gocheck,golang单元测试会分几篇文章来讲解,这次主要讲一下go语言使用testing做单元测试的一些方法
testing框架使用
- Go语言通过内置库testing包提供自动化测试功能。包内测试只要运行命令 go test,就能自动运行符合规则的测试函数
示例代码
package hello
import "testing"
func TestHelloWorld(t *testing.T) {
t.Log("hello world")
}
- 说下golang编写测试代码的规范:
- 每个测试文件必须以 _test.go 结尾,并使用go test命令执行测试模块,否则golang无法识别出测试模块
- 作为测试模块文件,必须导入testing包
- 功能测试方法必须以 Test 开头,一般是接被测方法的名字,如TestAdd,但不强制
- 测试用例会按照源代码中写的顺序依次执行
- 测试方法TestXxx()的参数是
testing.T
,用于收集记录测试结果 - testing中的断言需要自己编写,通过在方法中通过调用
testing.T
的Error
,Errorf
,FailNow
,Fatal
,FatalIf
方法来进识别测试通过与否,调用Log
方法用来记录测试的信息
命令行
- 单元测试使用 go test 命令启动
go test [-c] [-i] [build flags] [packages] [flags for test binary]
- 例如:
$ go test -v helloworld_test.go
=== RUN TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
helloworld_test.go:8: hello world
PASS
ok command-line-arguments 0.004s
- 下面是go test的一些常用传参方式
go test //默认执行当前目录下以xxx_test.go的测试文件
go test -v //可以看到详细的输出信息。
go test -v xxx_test.go //指定测试单个文件,但是该文件中如果调用了其它文件中的模块会报错
go test -v xxx_test.go xxx.go //指定测试单个文件和被测文件
go test -v -test.run Testxxx //指定某个测试函数运行,注意点是函数会用Testxxx去匹配所有包含Testxxx字符串作方法名的方法
go test -v -cover //显示覆盖的声明程度
- 当然还有其他例如用来做压力测试的bench参数,这个在后续的文章中讲解
实战应用
- 废话少说,我们直接用demo代码来讲解
待测模块 demo.go
//demo by terrychow
package main
import "fmt"
func Add(x,y int) int
{
return x+y
}
func main(){
z:=Add(1,2)
fmt.Print(z)
}
- 待测模块demo.go是一个简单的加法方式Add,这个也是本次测试的待测方法
- 由于待测模块demo.go为文件名,测试模块文件名必须以 _test 结尾,可命名为demo_test.go,不强制于被测文件前置同名,即可为other_test.go,测试模块文件路径可随意
测试模块 demo_test.go
//demo_test by terrychow
package main
import "testing"
func TestAdd(t *testing.T) {
s:=Add(1,2)
if s!=3{
t.Error("Expected 3, got ", s)
}
}
- 依照测试代码规范,我们编写出测试代码模块,然后使用go test命令来执行
go test demo_test.go demo.go -v -cover
- 输出为
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
coverage: 33.3% of statements
ok command-line-arguments 0.613s coverage: 33.3% of statements
- 调整一下代码断言值
//demo_test by terrychow
package main
import "testing"
func TestAdd(t *testing.T) {
s:=Add(1,2)
if s!=4{//此处修改了错误的返回
t.Error("Expected 4, got ", s)
}
}
- 输出FAIL信息
=== RUN TestAdd
--- FAIL: TestAdd (0.00s)
demo_test.go:8: Expected 4, got 3
FAIL
coverage: 33.3% of statements
FAIL command-line-arguments 0.668s
- 一个基于golang内置的测试框testing的单元测试已经实现了
进阶玩法
- 有时候我们针对一个方法的测试,会结合如路径覆盖、条件覆盖和语句覆盖等测试思路来进行对测试数据的组合测试,利用不同的数据组合验证覆盖方式,于是就有了组合测试的方式,针对Add方法,我们假设组合:
- 两个正数相加
- 两个负数相加
- 一正一负相加
示例代码
//demo by terrychow
package main
import "testing"
func TestGroupAdd(t *testing.T){
var testdata=[] struct{
arg1 int
arg2 int
sum int
}{
{1,1,2},
{-1,-1,-2},
{-1,1,0},
}
for _, test:=range testdata{
sum:=Add(test.arg1,test.arg2)
if sum!=test.sum{
t.Errorf("arg1 %v and arg2 %v not except sum %v",test.arg1,test.arg2,test.sum)
}
}
}
- 输出结果为
=== RUN TestGroupAdd
--- PASS: TestGroupAdd (0.00s)
PASS
coverage: 33.3% of statements
ok command-line-arguments 0.583s coverage: 33.3% of statements
- 假设其中一个为错误的数据,如
package main
import "testing"
func TestGroupAdd(t *testing.T){
var testdata=[] struct{
arg1 int
arg2 int
sum int
}{
{1,1,2},
{-1,-1,-1},//此处的sum为错误值
{-1,1,0},
}
for _, test:=range testdata{
sum:=Add(test.arg1,test.arg2)
if sum!=test.sum{
t.Errorf("arg1 %v and arg2 %v not except sum %v",test.arg1,test.arg2,test.sum)
}
}
}
- 输出FAIL信息
=== RUN TestGroupAdd
--- FAIL: TestGroupAdd (0.00s)
demo1_test.go:17: arg1 -1 and arg2 -1 not except sum -1
FAIL
coverage: 33.3% of statements
FAIL command-line-arguments 0.688s
- 会有对应的错误信息返回,说明错在哪一行
小结
- testing作为go语言内置的测试库,其当然还不仅仅是做单元测试,之后讲解的API和UI自动化测试,以及后面的压力、性能测试也会使用到testing,足见其核心作用
- 但testing也是有其限制性,没有良好的测试断言以及测试报告,靠使用者不断地写if else来判断显然麻烦,这也是下一篇文章要讲解go最常用单元测试框架gocheck,下回内容更精彩,请继续关注后续更新,谢谢大家
有疑问加站长微信联系(非本文作者)