Go语言开发(二十一)、GoMock测试框架
Go语言开发(二十一)、GoMock测试框架
一、GoMock简介1、GoMock简介GoMock是由Golang官方开发维护的测试框架,实现了较为完整的基于interface的Mock功能,能够与Golang内置的testing包良好集成,也能用于其它的测试环境中。GoMock测试框架包含了GoMock包和mockgen工具两部分,其中GoMock包完成对桩对象生命周期的管理,mockgen工具用来生成interface对应的Mock类源文件。 2、mockgen使用(1)mockgen工具选项 二、GoMock常用方法
type Call struct { t TestReporter // for triggering test failures on invalid call setup receiver interface{} // the receiver of the method call method string // the name of the method methodType reflect.Type // the type of the method args []Matcher // the args origin string // file and line number of call setup preReqs []*Call // prerequisite calls // Expectations minCalls,maxCalls int numCalls int // actual number made // actions are called when this Call is called. Each action gets the args and // can set the return values by returning a non-nil slice. Actions run in the // order they are created. actions []func([]interface{}) []interface{} } Call表示对mock对象的一个期望调用 三、GoMock应用示例1、interface编写定义一个需要mock的接口Repository,infra/db.go文件如下: package db type Repository interface { Create(key string,value []byte) error Retrieve(key string) ([]byte,error) Update(key string,value []byte) error Delete(key string) error } 2、mock文件生成mockgen生成mock文件: // Code generated by MockGen. DO NOT EDIT. // Source: ./infra/db.go // Package mock is a generated GoMock package. package mock import ( gomock "github.com/golang/mock/gomock" reflect "reflect" ) // MockRepository is a mock of Repository interface type MockRepository struct { ctrl *gomock.Controller recorder *MockRepositoryMockRecorder } // MockRepositoryMockRecorder is the mock recorder for MockRepository type MockRepositoryMockRecorder struct { mock *MockRepository } // NewMockRepository creates a new mock instance func NewMockRepository(ctrl *gomock.Controller) *MockRepository { mock := &MockRepository{ctrl: ctrl} mock.recorder = &MockRepositoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { return m.recorder } // Create mocks base method func (m *MockRepository) Create(key string,value []byte) error { ret := m.ctrl.Call(m,"Create",key,value) ret0,_ := ret[0].(error) return ret0 } // Create indicates an expected call of Create func (mr *MockRepositoryMockRecorder) Create(key,value interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,reflect.TypeOf((*MockRepository)(nil).Create),value) } // Retrieve mocks base method func (m *MockRepository) Retrieve(key string) ([]byte,error) { ret := m.ctrl.Call(m,"Retrieve",key) ret0,_ := ret[0].([]byte) ret1,_ := ret[1].(error) return ret0,ret1 } // Retrieve indicates an expected call of Retrieve func (mr *MockRepositoryMockRecorder) Retrieve(key interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,reflect.TypeOf((*MockRepository)(nil).Retrieve),key) } // Update mocks base method func (m *MockRepository) Update(key string,"Update",_ := ret[0].(error) return ret0 } // Update indicates an expected call of Update func (mr *MockRepositoryMockRecorder) Update(key,reflect.TypeOf((*MockRepository)(nil).Update),value) } // Delete mocks base method func (m *MockRepository) Delete(key string) error { ret := m.ctrl.Call(m,"Delete",_ := ret[0].(error) return ret0 } // Delete indicates an expected call of Delete func (mr *MockRepositoryMockRecorder) Delete(key interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,reflect.TypeOf((*MockRepository)(nil).Delete),key) } 3、MySQL.go文件package MySQL import "GoExample/GoMock/infra" type MySQL struct { DB db.Repository } func NewMySQL(db db.Repository) *MySQL { return &MySQL{DB: db} } func (mysql *MySQL) CreateData(key string,value []byte) error { return mysql.DB.Create(key,value) } func (mysql *MySQL) GetData(key string) ([]byte,error) { return mysql.DB.Retrieve(key) } func (mysql *MySQL) DeleteData(key string) error { return mysql.DB.Delete(key) } func (mysql *MySQL) UpdateData(key string,value []byte) error { return mysql.DB.Update(key,value) } 4、测试用例编写生成mock文件后就可以使用mock对象进行打桩测试,编写测试用例。 import ( "testing" "GoExample/GoMock/mock" "github.com/golang/mock/gomock" ) (2)mock控制器 ctrl := NewController(t) defer ctrl.Finish() mock对象创建时需要注入控制器,mock对象注入控制器的代码如下: ctrl := NewController(t) defer ctrl.Finish() mockRepo := mock_db.NewMockRepository(ctrl) (3)mock对象的行为注入 mockRepo.EXPECT().Retrieve(Any()).Return(nil,ErrAny) mockRepo.EXPECT().Create(Any(),Any()).Return(nil) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes,nil) 当批量Create对象时,可以使用Times关键字: mockRepo.EXPECT().Retrieve(Any()).Return(objBytes1,nil) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes2,nil) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes3,nil) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes4,nil) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes5,nil) (4)行为调用的保序 retrieveCall := mockRepo.EXPECT().Retrieve(Any()).Return(nil,ErrAny) createCall := mockRepo.EXPECT().Create(Any(),Any()).Return(nil).After(retrieveCall) mockRepo.EXPECT().Retrieve(Any()).Return(objBytes,nil).After(createCall) 通过InOrder关键字实现的保序示例代码: InOrder( mockRepo.EXPECT().Retrieve(Any()).Return(nil,nil) ) 通过InOrder关键字实现保序更简单,关键字InOrder是After的语法糖。 func InOrder(calls ...*Call) { for i := 1; i < len(calls); i++ { calls[i].After(calls[i-1]) } } 当mock对象行为的注入保序后,如果行为调用的顺序和其不一致,就会触发测试失败。如果在测试用例执行过程中,Repository方法的调用顺序如果不是按 Retrieve -> Create -> Retrieve的顺序进行,则会导致测试失败。 stubs := StubFunc(&mysql,mockdb) defer stubs.Reset() (6)测试用例编写 package MySQL import ( "testing" "GoExample/GoMock/mock" "fmt" "github.com/golang/mock/gomock" ) func TestMySQL_CreateData(t *testing.T) { ctr := gomock.NewController(t) defer ctr.Finish() var key string = "Hello" var value []byte = []byte("Go") mockRepository := mock_db.NewMockRepository(ctr) gomock.InOrder( mockRepository.EXPECT().Create(key,value).Return(nil),) mySQL := NewMySQL(mockRepository) err := mySQL.CreateData(key,value) if err != nil { fmt.Println(err) } } func TestMySQL_GetData(t *testing.T) { ctr := gomock.NewController(t) defer ctr.Finish() var key string = "Hello" var value []byte = []byte("Go") mockRepository := mock_db.NewMockRepository(ctr) gomock.InOrder( mockRepository.EXPECT().Retrieve(key).Return(value,nil),) mySQL := NewMySQL(mockRepository) bytes,err := mySQL.GetData(key) if err != nil { fmt.Println(err) } else { fmt.Println(string(bytes)) } } func TestMySQL_UpdateData(t *testing.T) { ctr := gomock.NewController(t) defer ctr.Finish() var key string = "Hello" var value []byte = []byte("Go") mockRepository := mock_db.NewMockRepository(ctr) gomock.InOrder( mockRepository.EXPECT().Update(key,) mySQL := NewMySQL(mockRepository) err := mySQL.UpdateData(key,value) if err != nil { fmt.Println(err) } } func TestMySQL_DeleteData(t *testing.T) { ctr := gomock.NewController(t) defer ctr.Finish() var key string = "Hello" mockRepository := mock_db.NewMockRepository(ctr) gomock.InOrder( mockRepository.EXPECT().Delete(key).Return(nil),) mySQL := NewMySQL(mockRepository) err := mySQL.DeleteData(key) if err != nil { fmt.Println(err) } } 5、测试进入测试用例目录: 6、测试结果查看生成测试覆盖率的 profile 文件: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |