golang 单元测试
- 文件格式:go单元测试,有固定的名称格式,所有以_test.go为后缀名的源文件在执行go build 时不会被构建成包的一部分,他们是go test 测试的一部分。
测试函数
- 函数格式:每个测试函数是以Test为函数名称前缀。
每个测试函数必须导入testing包。
func TestName(t *testing.T) {
// ...
}
基准测试(benchmark)
- 函数格式:函数前缀名称Benchmark。
- 作用:用于衡量函数的性能。会多次运行基准函数,计算一个平均的执行时间。
示例函数
- 函数前缀名称Example
- 作用:提供一个由编译器保证正确性的示例文档。
命令介绍
- 参数-v可用于打印每个测试函数的名字和运行时间:
go test -v
2.-run 测试制定的函数名称
go test -v -run="函数名称"
3.-cover 查看测试覆盖率
go test -v -run="" -cover
go test -cover golang.guazi-corp.com/finance/lego/model/imports/list
4.最后统统执行一遍全部测试用例
go test
5.示例
go test -run '' # Run all tests.
go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar".
go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
表格驱动测试(自动生成)
开源:gotests开源地址
安装gotests
- 执行下面命令
go get -u github.com/cweill/gotests
2.将下载的gotests目录,配置到PATH环境变量中
export PATH=$PATH:$GOBIN:$GOPATH:$MYSQLBIN
gotests使用
-all
generate tests for all functions and methods
为所有函数和方法生成go测试
-excl string
为没有匹配的函数和方法生成go测试
regexp. generate tests for functions and methods that don't match. Takes precedence over -only, -exported, and -all
-exported
为导出的函数和方法生成go测试。
generate tests for exported functions and methods. Takes precedence over -only and -all
-i
在错误消息中打印测试输入
print test inputs in error messages
-nosubtests
禁用子测试生成。仅适用于Go 1.7+
disable generating tests using the Go 1.7 subtests feature
-only string
正则表达式。生成仅匹配的函数和方法的测试。优先于-all
regexp. generate tests for functions and methods that match only. Takes precedence over -all
-template_dir string
可选的。包含自定义测试代码模板的目录的路径
optional. Path to a directory containing custom test code templates
-w
将输出写入(测试)文件而不是stdout
write output to (test) files instead of stdout
常用操作
1.进入对应的要被生成单元测试的目录
2.在该目录下执行命令
3.生成指定函数的单元测试,输出到命令行,然后复制粘贴到目标处。
gotests -only "函数名称" 文件名称.go
4.生成全部测试函数
gotests -all 文件名称.go
gotests -only " 方法名称" 文件名称
gotests -all -w origin.go, origin_test.go
会自动创建在当前目录下,并自动生成测试代码,只需要将不同的测试数据按照tests定义的结构写在//TODO:Add test cases下面,测试用例就完成了。
mock测试
参考文章
mock是单元测试中常用的一种测试手法,mock对象被定义,并能够替换掉真实的对象被测试的函数所调用。
而mock对象可以被开发人员很灵活的指定传入参数,调用次数,返回值和执行动作,来满足测试的各种情景假设。
使用场景
- 依赖的服务返回不确定的结果,如获取当前时间。
- 依赖的服务返回状态中有的难以重建或复现,比如模拟网络错误。
- 依赖的服务搭建环境代价高,速度慢,需要一定的成本,比如数据库,web服务
- 依赖的服务行为多变。
为了保证测试的轻量以及开发人员对测试数据的掌控,采用mock来斩断被测试代码中的依赖不失为一种好方法。
每种编程语言根据语言特点其所采用的mock实现有所不同。
mockery
安装
go get github.com/vektra/mockery/.../
使用
Run: mockery -name=Stringer生成的mock名称 and the following will be output to mocks/Stringer.go
1、生成mock文件,默认在./mocks目录下
mockery -name=Mocker接口名
2、生成mock输出到控制台
mockery -name=接口名 -print
代码处理:
1.业务接口
- 1.定义被mock的接口
- 2.定义类(实现接口)
- 3.定义个方法New(),返回该类对象
// 1.定义接口
type Driver interface {
Add(*ImportSet) (int64, error)
}
// 2.定义类,实现该接口
type driverImp struct{}
//3.定义方法,创建该类对象
func NewDriver() Driver {
return &driverImp{}
}
2.生成mock方法
mockery -name=接口名 -print
生成要被mock的方法
3.service层调用处
// 用创建对象的方法,获取对象,调用接口方法
NewDriver().QueryImportDetails(req.ImportUuid)
4.单元测试
func TestFunc(t *testing.T) {
//保存原来的对象helper.ListDrv就是newDrv()
oldFd := helper.ListDrv
//最后还原对象
defer func() {
helper.ListDrv = oldFd
}()
type args struct {
ctx context.Context
req
}
tests := []struct {
name string
args args
want *****
wantErr bool
//在表驱动中,写mock方法
mock func()
}{
{
name: "error",
args: args{
ctx: context.Background(),
req: **,
},
wantErr: false,
want: ***
mock: func() {
mfd := &listmock.Driver{}
mfd.On("QueryCount").Return(int64(0), nil)
mfd.On("QueryList", mock.Anything, mock.Anything).Return([]list.ImportList{{UUID: "xxx", Description: "ddddd"}}, nil)
//还原对象
helper.ListDrv = mfd
},
},
},
// 测试用例
for _, tt := range tests {
// 执行mock方法
tt.mock()
t.Run(tt.name, func(t *testing.T) {
里面的单元测试内容不变
}
常用命令
-v是显示出详细的测试结果,
-cover 显示出执行的测试用例的测试覆盖率
1 测试单个文件,一定要带上被测试的原文件
go test -v -cover file1_test.go file.go
2 测试单个函数方法
go test -v -cover -run TestFuncName
3 测试整个api包
在包统计目录下
go test -v -cover ./api/...
4、自动生成测试用例,为指定的函数生成单元测试,输出到控制台
gotests -only "函数名称" file(源文件名称).go
5、自动生成测试用例,为指定文件生成单元测试,输出到文件中
gotests -all -w origin.go, origin_test.go
6、生成mock
//输出到控制台
mockery -name=接口名 -print
//输出到./mocks/接口名.go文件中
mockery -name=接口名
有疑问加站长微信联系(非本文作者)