Golang单元测试利器:gomock工具详解

发表时间: 2024-07-01 22:51

在做单测时为了解耦相关依赖,需要mock工具,这里推荐第三方库:

https://github.com/golang/mock

https://github.com/uber-go/mock

目前golang/mock库停止维护,由其分支uber-go/mock继续维护,用法相同。

先安装工具mockgen

安装命令

go install go.uber.org/mock/mockgen@latest

查看mockgen工具版本

mockgen -version

输出:v0.4.0

mockgen

可以查看相关命令

mockgen has two modes of operation: source and reflect.Source mode generates mock interfaces from a source file.It is enabled by using the -source flag. Other flags thatmay be useful in this mode are -imports and -aux_files.Example:        mockgen -source=foo.go [other options]Reflect mode generates mock interfaces by building a programthat uses reflection to understand interfaces. It is enabledby passing two non-flag arguments: an import path, and acomma-separated list of symbols.Example:        mockgen database/sql/driver Conn,Driver  -aux_files string        (source mode) Comma-separated pkg=path pairs of auxiliary Go source files.  -build_flags string        (reflect mode) Additional flags for go build.  -copyright_file string        Copyright file used to add copyright header  -debug_parser        Print out parser results only.  -destination string        Output file; defaults to stdout.  -exclude_interfaces string        Comma-separated names of interfaces to be excluded  -exec_only string        (reflect mode) If set, execute this reflection program.  -imports string        (source mode) Comma-separated name=path pairs of explicit imports to use.  -mock_names string        Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.  -package string        Package of the generated code; defaults to the package of the input with a 'mock_' prefix.  -prog_only        (reflect mode) Only generate the reflection program; write it to stdout and exit.  -self_package string        The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.  -source string        (source mode) Input Go source file; enables source mode.  -typed        Generate Type-safe 'Return', 'Do', 'DoAndReturn' function  -version        Print version.  -write_generate_directive        Add //go:generate directive to regenerate the mock  -write_package_comment        Writes package documentation comment (godoc) if true. (default true)  -write_source_comment        Writes original file (source mode) or interface names (reflect mode) comment if true. (default true)

用法

mockgen支持两种模式:源文件和反射

1、Source mode

通过源文件生成mock接口

示例

mockgen -source=foo.go [other options]

2、Reflect mode

反射模式通过构建一个使用反射来理解接口的程序来生成mock接口。

示例

mockgen database/sql/driver Conn,Driver

测试用例

新建测试目录db,文件db.go、db_getter.go

db.go

package dbtype DBInterface interface {	Get(val string) string}type DB struct {}func (d *DB) Get(val string) string {	return val}

db_getter.go

package dbfunc GetFromDB(db DB) string {	return db.Get("example")}

使用mockgen工具对所需mock的interface生成mock文件

在db目录下执行

mockgen -source ./db.go -destination ./mock/db_mock.go
  • -source mock接口文件来源
  • -destination 生成mock文件路径,不设置则打印在标准输出中
  • -package 指定包名,不指定默认为mock_文件名

在db/mode目录下生成db_mock.go

// Code generated by MockGen. DO NOT EDIT.// Source: ./db.go//// Generated by this command:////	mockgen -source ./db.go -destination ./mock/db_mock.go//// Package mock_db is a generated GoMock package.package mock_dbimport (	reflect "reflect"	gomock "go.uber.org/mock/gomock")// MockDBInterface is a mock of DBInterface interface.type MockDBInterface struct {	ctrl     *gomock.Controller	recorder *MockDBInterfaceMockRecorder}// MockDBInterfaceMockRecorder is the mock recorder for MockDBInterface.type MockDBInterfaceMockRecorder struct {	mock *MockDBInterface}// NewMockDBInterface creates a new mock instance.func NewMockDBInterface(ctrl *gomock.Controller) *MockDBInterface {	mock := &MockDBInterface{ctrl: ctrl}	mock.recorder = &MockDBInterfaceMockRecorder{mock}	return mock}// EXPECT returns an object that allows the caller to indicate expected use.func (m *MockDBInterface) EXPECT() *MockDBInterfaceMockRecorder {	return m.recorder}// Get mocks base method.func (m *MockDBInterface) Get(val string) string {	m.ctrl.T.Helper()	ret := m.ctrl.Call(m, "Get", val)	ret0, _ := ret[0].(string)	return ret0}// Get indicates an expected call of Get.func (mr *MockDBInterfaceMockRecorder) Get(val any) *gomock.Call {	mr.mock.ctrl.T.Helper()	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDBInterface)(nil).Get), val)}


编写测试

db_getter_test.go

package dbimport (	mock_db "demo/db/mock"	"testing"	"github.com/stretchr/testify/assert"	"go.uber.org/mock/gomock")func TestGetFromDB(t *testing.T) {	//创建gomock顶级控制器,用来记录后续mock对象的范围,生命周期等信息,另外它是协成安全的	ctrl := gomock.NewController(t)	// 延迟执行期望断言	defer ctrl.Finish()	//创建一个mock实例	mockDb := mock_db.NewMockDBInterface(ctrl)	//gomock.InOrder 控制执行顺序	gomock.InOrder(		mockDb.EXPECT().			Get(gomock.Eq("example")). //get方法参数			Return("data").            //设置期望返回值			Times(1),                  //执行次数	)	got := GetFromDB(mockDb)	//比较实际返回的值和预期是否相等	assert.Equal(t, "data", got)}

常用参数:

  • gomock.Eq(value):表示一个等价于value值的参数
  • gomock.Not(value):表示一个非value值的参数
  • gomock.Any():表示任意值的参数
  • gomock.Nil():表示空值的参数
  • Times(n): 执行n次
  • AnyTimes():执行任意次

更多参数移步官方https://pkg.go.dev/github.com/golang/mock/gomock#pkg-index

测试

执行

go test -v

结果:

=== RUN TestClear

--- PASS: TestClear (0.00s)

PASS

ok demo 0.309s