GoMock工具是Golang官方提供的针对接口的代码生成测试工具。在实际的单元测试过程中,通常会选择Mock掉数据库(DB/KV)、外部服务调用操作部分,将这部分功能留在集成测试中完成。

比如我们将数据操作类型抽象成接口Creator、Updater、Deleter等,借助接口的组合功能,针对我们需要的功能进行组合开发。在测试过程中,我们可以借助GoMock工具生成对应的测试辅助代码,再结合testfy使用。

文档:https://pkg.go.dev/github.com/golang/mock

例子

Repository 定义curd接口

  1. package afterShip
  2. type Repository interface {
  3. Create(key string, value []byte) error
  4. Get(key string) ([]byte, error)
  5. Update(key string, value []byte) error
  6. Delete(key string) error
  7. }

�config 依赖Repository,实现配置文件的增删改查

  1. package afterShip
  2. type config struct {
  3. db Repository
  4. }
  5. func newConfig(db Repository) *config {
  6. return &config{
  7. db: db,
  8. }
  9. }
  10. func (c *config) getConfig(key string) ([]byte, error) {
  11. return c.db.Get(key)
  12. }
  13. func (c *config) updateConfig(key string, value []byte) error {
  14. return c.db.Update(key, value)
  15. }
  16. func (c *config) createConfig(key string, value []byte) error {
  17. return c.db.Update(key, value)
  18. }
  19. func (c *config) deleteConfig(key string) error {
  20. return c.db.Delete(key)
  21. }

使用gomock 生成Repository 接口的一个测试用实现

  1. mockgen -source=repository.go > repository_mock.go -package afterShip
  • -source:指定源文件
  • -destination:指定生成代码存放的文件
  • -package:指定生成代码的package name
  • -imports:指定要导入的包,多个用”,” 隔开
  • -self_package:此标志的目的是通过尝试包含自己的包,告诉mockgen要排除哪个导入,防止出现”循环导入”, -self_package xxx
  • -copyright_file:版本声明,用于写入文件头
  • -write_package_comment:是否生成包注释,用于godoc,默认为true

最后,写测试用例 config_test

  1. package afterShip
  2. import (
  3. "github.com/stretchr/testify/assert"
  4. "testing"
  5. "github.com/golang/mock/gomock"
  6. )
  7. func TestConfig(t *testing.T) {
  8. ctl := gomock.NewController(t)
  9. defer ctl.Finish()
  10. repository := NewMockRepository(ctl)
  11. cfg := newConfig(repository)
  12. // 预设值
  13. repository.EXPECT().
  14. // 不确定入参,可使用 gomock.Any() 作为参数
  15. // 确定入参 可使用 gomock.AssignableToTypeOf() 转为该类型参数
  16. Get("k1"). // 方法名 与参数
  17. Return([]byte("v1"),nil). // 设置返回值
  18. AnyTimes() // 允许Get被调用0次或多次,默认必须调用一次(仅一次)
  19. // 测试config
  20. res,err := cfg.getConfig("k1")
  21. assert.Nil(t, err)
  22. assert.Equal(t, res,[]byte("v1"))
  23. }

执行该用例

  1. mac@weideMacBook-Pro afterShip % go test -v -run TestConfig
  2. === RUN TestConfig
  3. --- PASS: TestConfig (0.00s)
  4. PASS
  5. ok commons/afterShip 0.014s

func

  1. // 给定的方法,必须按顺序进行调用
  2. func InOrder(calls ...*Call)

type Call

  1. type Call struct {}
  2. func (c *Call) AnyTimes() *Call // AnyTimes允许期望被调用0次或更多次
  3. func (c *Call) MaxTimes(n int) *Call // 设置最大调用次数
  4. func (c *Call) MinTimes(n int) *Call // 设置最小调用次数
  5. func (c *Call) Return(rets ...interface{}) *Call // 设置返回值
  6. func (c *Call) SetArg(n int, value interface{}) *Call // 设置入参
  7. func (c *Call) Times(n int) *Call // 确切的设置方法被调用的次数

type Controller

Controller表示模拟生态系统的顶级控制。它定义了模拟对象的范围和生存期,以及它们的期望。从多个goroutines调用Controller的方法是安全的。每个测试都应该创建一个新的Controller并通过延迟调用Finish。

  1. type Controller struct {
  2. T TestHelper
  3. }
  4. func NewController(t TestReporter) *Controller
  5. func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context)