go测试框架gomock的使用

2022-06-30 10:26:45 浏览数 (1)

一。介绍

gomock是golang官方开发维护的接口级别的mock方案,包含了GoMock包和mockgen工具两部分,其中GoMock包完成对桩对象生命周期的管理,mockgen工具用来生成interface对应的Mock类源文件。要使用gomock的一个前提是模块之间务必通过接口进行依赖,而不是依赖具体实现,否则mock会非常困难。这个工具目前业界用的并不多,主要是局限性太大,所以我们只需要简单了解一下如何使用就行。

github地址:https://github.com/golang/mock

二。使用

1. 安装

代码语言:txt复制
go get -u github.com/golang/mock/gomock
go install github.com/golang/mock/mockgen

2. Quick start

第一步:定义接口,注意这个工具之支持对接口生成mock,其他的类型方法不支持。
代码语言:txt复制
package dao

import (
	"fmt"
	_ "github.com/golang/mock/mockgen/model"
)

// 用户持久层操作对象
type UserDao interface {
	Update(id, name, phoneNumber string) int64
	Update2(param ...string) int64
	Add(name, phoneNumber string) (int64, error)
	Select(id string) int64
	Delete(id string) int64
}

type userDao struct{}

func (u userDao) Update(id, name, phoneNumber string) int64 {
	fmt.Println(id, name, phoneNumber)
	return 1
}
第二步:使用命令生成mock类
代码语言:txt复制
mockgen  -destination mock_user_dao.go -package dao -source user_dao.go 

如果觉得命令太长了,可以直接在UserDao上面加个go:generate的注释如下

代码语言:txt复制
package dao

import (
	"fmt"
	_ "github.com/golang/mock/mockgen/model"
)

//go:generate mockgen -destination mock_user_dao.go -package dao -source user_dao.go
// 用户持久层操作对象
type UserDao interface {
	Update(id, name, phoneNumber string) int64
	Update2(param ...string) int64
	Add(name, phoneNumber string) (int64, error)
	Select(id string) int64
	Delete(id string) int64
}
...

然后在当前包的目录下bash执行go generate也是可以生成mock文件的。

第三步:自定义mock实现并进行断言
代码语言:txt复制
package dao

import (
	"errors"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"testing"
)

/**
 * @Description
 * @Author guirongguo
 * @Email 
 * @Date 2021/8/31 20:37
 **/

func Test_userDao(t *testing.T) {

	// step1. 初始化一个mock controller
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mockObj := NewMockUserDao(mockCtrl)

	// step2. 定义mock的具体执行逻辑
	// 1.参数支持Eq,Any,Not,Nil
	//Eq(value) 表示与 value 等价的值。
	//Any() 可以用来表示任意的入参。
	//Not(value) 用来表示非 value 以外的值。
	//Nil() 表示 None 值
	//
	// 2.mock方法调用次数支持如下
	//Times() 断言 Mock 方法被调用的次数。
	//MaxTimes() 最大次数。
	//MinTimes() 最小次数。
	//AnyTimes() 任意次数(包括 0 次)
	//
	// 3.返回值支持
	// Return 返回确定的值
	// Do Mock 方法被调用时,要执行的操作吗,忽略返回值。
	// DoAndReturn 可以动态地控制返回值。
	c1 := mockObj.EXPECT().Update("001", "ggr", "10010").Return(int64(1))
	c2 := mockObj.EXPECT().Update("001", "ggr", gomock.Any()).Return(int64(1))
	mockObj.EXPECT().Add("", "1001").Return(int64(0), errors.New("name is invalid")).AnyTimes()
	mockObj.EXPECT().Add("1001", gomock.Not("1001")).Return(int64(0), errors.New("phone_number is invalid")).MinTimes(1)
	mockObj.EXPECT().Add(gomock.Eq("1001"), "1001").Return(int64(0), errors.New("name is empty")).Times(1)
	mockObj.EXPECT().Select(gomock.Any()).Return(int64(1)).MaxTimes(2)
	mockObj.EXPECT().Update2(gomock.Any()).Return(int64(1)).Times(2)

	mockObj.EXPECT().Delete("1001").Do(func(i string) {
		t.Log("delete id=1001")
	})

	mockObj.EXPECT().Delete(gomock.Not("1001")).DoAndReturn(func(i string) int64 {
		return int64(1)
	})

	// step3. 限制stub函数的调用顺序,如果顺序不一致,则报错
	gomock.InOrder(c1, c2)

	// step4. 测试验证
	ret1 := mockObj.Update("001", "ggr", "10010")
	ret2 := mockObj.Update("001", "ggr", "10020")
	ret3 := mockObj.Update2("1", "")
	ret4 := mockObj.Update2("")
	ret5 := mockObj.Select("")
	ret6, err1 := mockObj.Add("1001", "1001")
	ret7, err2 := mockObj.Add("1001", "1002")
	ret8 := mockObj.Delete("1001")
	ret9 := mockObj.Delete("1002")

	// step5. 断言
	assert.Equal(t, ret1, int64(1))
	assert.Equal(t, ret2, int64(1))
	assert.Equal(t, ret3, int64(1))
	assert.Equal(t, ret4, int64(1))
	assert.Equal(t, ret5, int64(1))
	assert.Equal(t, ret6, int64(0))
	assert.Equal(t, ret7, int64(0))
	assert.Equal(t, ret8, int64(0))
	assert.Equal(t, ret9, int64(1))
	assert.NotNil(t, err1)
	assert.NotEmpty(t, err2)
}

自定义mock实现主要包含了自定义参数,自定义返回值,自定义mock调用次数以及调用顺序。

(1)自定义参数

参数支持Eq,Any,Not,Nil,分别代表一下含义:

  • Eq(value) 用于参数为固定值的场景。
  • Any() 用于任意参数的场景。
  • Not(value) 用于表示参数非 value 以外的值场景。
  • Nil() 用于表示参数None 值的场景

(2)自定义返回值

返回值支持如下几种:

  • Return 用于返回确定的值的场景
  • Do 用于无返回值的场景。
  • DoAndReturn 用于可以动态地控制返回值。

(3)自定义mock调用次数

mock调用次数支持如下几种场景:

  • Times() 断言 Mock 方法被调用的次数, 指定次数。
  • MaxTimes() 最大次数。
  • MinTimes() 最小次数。
  • AnyTimes() 任意次数(包括 0 次)

(4)自定义mock调用顺序

当存在多个mock之间相互调用的情况时,可以通过一下2种方式定义mock执行的顺序:

  • 直接在函数后面接After
  • 使用gomock.InOrder设置执行顺序

更多使用方法请参考官方文档:https://pkg.go.dev/github.com/golang/mock/gomock#pkg-examples

0 人点赞