golang 单元测试
- 文件格式:go单元测试,有固定的名称格式,所有以_test.go为后缀名的源文件在执行go build 时不会被构建成包的一部分,他们是go test 测试的一部分。
测试函数
-
函数格式:每个测试函数是以Test为函数名称前缀。
每个测试函数必须导入testing包。
func TestName(t *testing.T) { // ... }
基准测试(benchmark)
- 函数格式:函数前缀名称Benchmark。
- 作用:用于衡量函数的性能。会多次运行基准函数,计算一个平均的执行时间。
示例函数
- 函数前缀名称Example
- 作用:提供一个由编译器保证正确性的示例文档。
命令介绍
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
- 执行下面命令
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=接口名