我们一般会用gotests 来生成golang测试代码,但是这个工具并不是那么好用,遇到了下面这些问题:
1,gomock 支持不友好,没有统一的模板,导致测试代码格式不统一,阅读性差
比如:
1.1 gomock 初学者喜欢写全局的mock代码,特别是引入了,anyTime()以后,会导致sutests 之间相互影响,提升了问题排查难度。
1.2 一些参数,没有统一的管理传值,规范,比如context,很可能会导致mock调用之间值,传错
2,生成的代码golangci-lint 不通过
golangci-lint run . party_test.go:24:20: Using the variable on range scope tt in function literal (scopelint) visitorLister: tt.fields.visitorLister, ^ party_test.go:25:20: Using the variable on range scope tt in function literal (scopelint) greeter: tt.fields.greeter, ^ party_test.go:27:30: Using the variable on range scope tt in function literal (scopelint) if err := s.GreetVisitors(tt.args.justNice); (err != nil) != tt.wantErr { ^ party_test.go:28:74: Using the variable on range scope tt in function literal (scopelint) t.Errorf("PartyService.GreetVisitors() error = %v, wantErr %v", err, tt.wantErr)
3,需要在case段和 driving 段同时添加代码,写case 来回滚动屏幕,不方便,特别是case很多的时候。
package main
import (
"fmt"
"testing"
"party/greet"
mock_greet "party/greet/mock"
"github.com/golang/mock/gomock"
)
func TestPartyService_GreetVisitors(t *testing.T) {
type fields struct {
visitorLister greet.VisitorLister
greeter greet.Greeter
}
type args struct {
justNice bool
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockedVisitorLister := mock_greet.NewMockVisitorLister(ctrl)
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
{
name: "ccase1",
fields: fields{
visitorLister: mockedVisitorLister,
},
wantErr: true,
},
}
for _, tt := range tests {
mockedVisitorLister.EXPECT().ListVisitors(greet.NiceVisitor).Return([]greet.Visitor{{"Peter", "TheSmart"}}, nil)
t.Run(tt.name, func(t *testing.T) {
s := &PartyService{
visitorLister: tt.fields.visitorLister,
greeter: tt.fields.greeter,
}
if err := s.GreetVisitors(tt.args.justNice); (err != nil) != tt.wantErr {
t.Errorf("PartyService.GreetVisitors() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
4,gomock 函数入参和返回值ide提示不友好,导致,写mock时候找这些类型,困难。
为了解决上述问题,提升写test的效率,开发了一个支持mock的table driving test 工具。
源码地址:https://github.com/xiazemin/autotest
安装:
go get -u github.com/xiazemin/autotest
入门example
https://github.com/xiazemin/tabel_drving_test_learn
使用介绍
下载后,在vscode 中,鼠标,右键,generate unite test for function 就可以生成,带gomock的和test case 信息的代码。
主要原理介绍,针对上面的测试代码可以做下调整,把
ctrl := gomock.NewController(t)
defer ctrl.Finish()
放到case运行式的循环中,通过作用域的限制,消除case间的影响。
然后通过闭包的方式,把mock代码写到case里,避免了代码的来回切换
tests := []struct {
name string
fields fields
prepare func(fields *fields, args *args)
args args
wantErr bool
}
if tt.prepare != nil {
tt.prepare(&tt.fields, &tt.args)
}
通过上面的调整,我们可以专注的写case,但是,上述代码,很多都是重复劳动,所以可以考虑自动生成。自动生成的原理,大致经过了,编译,链接,渲染几个过程,生成的最终代码如下
package main
import (
"party/greet"
"testing"
mock_greet "party/greet/mock"
"github.com/golang/mock/gomock"
)
func TestPartyService_GreetVisitors(t *testing.T) {
type fields struct {
visitorLister *mock_greet.MockVisitorLister
greeter *mock_greet.MockGreeter
}
type args struct {
justNice bool
}
tests := []struct {
name string
fields fields
prepare func(fields *fields, args *args)
args args
wantErr bool
}{
// TODO: Add test cases.
{
name: "case1",
prepare: func(fields *fields, args *args) {
gomock.InOrder(
fields.visitorLister.EXPECT().ListVisitors(greet.VisitorGroup{}).Return([]greet.Visitor{},
error,
).Times(2),
fields.visitorLister.EXPECT().ListVisitors2(&greet.VisitorGroup{}).Return([]*greet.Visitor{},
error,
).Times(2),
fields.greeter.EXPECT().Hello(string).Return(string).Times(2),
fields.greeter.EXPECT().Hello1(string,
string,
).Return(string,
string,
).Times(2),
)
},
},
}
for _, tt := range tests {
tt := tt
ctrl := gomock.NewController(t)
defer ctrl.Finish()
tt.fields.visitorLister = mock_greet.NewMockVisitorLister(ctrl)
tt.fields.greeter = mock_greet.NewMockGreeter(ctrl)
if tt.prepare != nil {
tt.prepare(&tt.fields, &tt.args)
}
s := &PartyService{
visitorLister: tt.fields.visitorLister,
greeter: tt.fields.greeter,
}
if err := s.GreetVisitors(tt.args.justNice); (err != nil) != tt.wantErr {
t.Errorf("%q. PartyService.GreetVisitors() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
}
}
研发可以专注写case段代码,仅仅需要调整下面这一部分,不用观其他无关逻辑。
// TODO: Add test cases.
{
name: "case1",
prepare: func(fields *fields, args *args) {
gomock.InOrder(
fields.visitorLister.EXPECT().ListVisitors(greet.VisitorGroup{}).Return([]greet.Visitor{},
error,
).Times(2),
fields.visitorLister.EXPECT().ListVisitors2(&greet.VisitorGroup{}).Return([]*greet.Visitor{},
error,
).Times(2),
fields.greeter.EXPECT().Hello(string).Return(string).Times(2),
fields.greeter.EXPECT().Hello1(string,
string,
).Return(string,
string,
).Times(2),
)
},
},
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!