使用testify库中的mock实现
准备一个接口
db.go
package mock
import "fmt"
type DB interface {
Get(key string) (int, error)
Add(key string, value int) error
}
func GetFromDB(db DB, key string) int {
val, err := db.Get(key)
if err != nil {
fmt.Println(err)
return -1
}
return val
}
db_test.go
package mock
import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"
)
// mock对象
// 需要组合mock.Mock
type TestifyMockDB struct {
mock.Mock
}
// 实现DB接口,重写方法
func (t *TestifyMockDB) Get(key string) (int, error) {
// 调用mock.Mock的Call方法,传入参数
// arguments保存返回值
arguments := t.Called(key)
return arguments.Int(0), arguments.Error(1) // 按序获取对应类型的返回值
}
func (t *TestifyMockDB) Add(key string, value int) error {
arguments := t.Called(key, value)
return arguments.Error(0)
}
func TestGetFromDB(t *testing.T) {
mockDB := TestifyMockDB{}
// 打桩
// 调用Get方法传入yimin时,返回1,nil
mockDB.On("Get", "yimin").Return(1, nil)
val, err := mockDB.Get("yimin")
assert.NoError(t, err)
assert.Equal(t, 1, val)
}
PS D:\dev\project\common-go\mock> go test -v -run=TestGetFromDB
=== RUN TestGetFromDB2
--- PASS: TestGetFromDB2 (0.00s)
PASS
ok common-go/mock 0.054s
这种方式需要我们自己定义Mock对象,并实现接口
第二种方式使用go官方的库gomock实现,通过命令实现自动生成Mock对象,相对手动实现较为简单
使用gomock库实现
安装gomock
go install github.com/golang/mock/mockgen@v1.6.0
执行命名生成文件
mockgen -source .\db.go -destination db_mock.go
以上命令会在db.go同级目录生成db_mock.go文件
大概就是给我们自动生成了Mock对象,并且实现了DB接口
文件内容不用改动
// Code generated by MockGen. DO NOT EDIT.
// Source: .\db.go
// mockgen -source .\db.go -destination db_mock.go
package mock
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockDB is a mock of DB interface.
type MockDB struct {
ctrl *gomock.Controller
recorder *MockDBMockRecorder
}
// MockDBMockRecorder is the mock recorder for MockDB.
type MockDBMockRecorder struct {
mock *MockDB
}
// NewMockDB creates a new mock instance.
func NewMockDB(ctrl *gomock.Controller) *MockDB {
mock := &MockDB{ctrl: ctrl}
mock.recorder = &MockDBMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDB) EXPECT() *MockDBMockRecorder {
return m.recorder
}
// Add mocks base method.
func (m *MockDB) Add(key string, value int) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Add", key, value)
ret0, _ := ret[0].(error)
return ret0
}
// Add indicates an expected call of Add.
func (mr *MockDBMockRecorder) Add(key, value interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockDB)(nil).Add), key, value)
}
// Get mocks base method.
func (m *MockDB) Get(key string) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", key)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Get indicates an expected call of Get.
func (mr *MockDBMockRecorder) Get(key interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDB)(nil).Get), key)
}
接下来还是编写测试方法
func TestGetFromDB2(t *testing.T) {
ctl := gomock.NewController(t)
mockDB := NewMockDB(ctl) // 这是自动生成的db_mock.go中的方法
// 打桩
mockDB.EXPECT().Get("yimin").Return(1, nil).Times(1)
res := GetFromDB(mockDB, "yimin")
assert.Equal(t, 1, res)
// 返回前执行一段函数
mockDB.EXPECT().Get("yimin2").DoAndReturn(func(key string) (int, error) {
t.Logf("Get called with %s", key)
return 2, nil
})
yimin2Val, err := mockDB.Get("yimin2")
assert.NoError(t, err)
assert.Equal(t, 3, yimin2Val)
// 任意参数
mockDB.EXPECT().Add(gomock.Any(), gomock.Any()).Return(nil).Times(1)
err = mockDB.Add("yimin", 1)
assert.NoError(t, err) // 不返回任何错误
t.Logf("Add called with yimin and 1")
// 需要注意的是,通过EXPECT构造的用例必须都要调用,否则会报错
// 以下代码加不加都一样
ctl.Finish() // 断言mockDB的EXPECT()方法是否都已经被调用
}