gomock 简述

ps:本篇内容仅为当前知识的总结,可能存在某些错误。

What is ?

mock 原义为嘲弄、嘲笑。有一种滑稽的小丑的感觉。在代码中通常表示单元测试中一些伪造的对象。gomock可以创建一些实现了复杂接口的对象,但是这些对象并没有真正的底层逻辑,仅仅应用于简化单元测试。

Why we need ?

在golang的单元测试中难免会遇到一些上层代码调用下层依赖的场景,但是如果在单元测试中真实的创建如数据库之类复杂的依赖会降低单元测试的性能与健壮性并提升代码复杂度。

在golang中这些依赖实现了相关的接口,上层代码并不关心下层具体实现,使用gomock通过简单的逻辑来模拟下层接口中的各种方法,实现MockXXX对象。将MockXXX对象作为单元测试的依赖,使得单元测试仅关注本层代码逻辑。

How to use ?

生成 Mock 对象

可以使用命令行mockgen命令或者在相关接口代码前添加注释//go:generate...然后执行go generate命令。

设计Mock对象相关方法的逻辑

通过生成mock对象,我们可以看到MockXXX的相关代码已经生成。

例如:

1
2
3
4
5
6
7
type Foo interface {
  Bar(x int) int
}

func SUT(f Foo) {
 // ...
}

MockFoo 结构体、NewMockFooMockFoo.Bar方法相关的代码都已经生成,但是这些代码并没有定义具体逻辑,具体调用MockFoo.Bar方法会怎样处理入参返回什么结果需要我们在单元测试中定义。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func TestFoo(t *testing.T) {
  ctrl := gomock.NewController(t)

  // Assert that Bar() is invoked.
  defer ctrl.Finish()

  m := NewMockFoo(ctrl)

  // Asserts that the first and only call to Bar() is passed 99.
  // Anything else will fail.
  m.
    EXPECT().
    Bar(gomock.Eq(99)).
    Return(101)

  SUT(m)
}

其中m.EXPECT().Bar(XXX)...开头的代码就是对MockFoo的Bar方法进行设计的逻辑,这里设定对于Bar()方法的第一次调用如果入参是99则返回101,如果入参为其他值则会导致macher不匹配引起失败。

ctrl.Finish()用来断言Foo.Bar()方法是否使用过,在go version 1.14+ and mockgen version 1.5.0+中将被自动注册在cleanup()中。

mock可以对方法设计更为复杂的逻辑这方面网络上资料很多。

Mock 原理

mock原理应该挺有趣和反射应该有关,但目前还未细看。

参考

https://www.cnblogs.com/li-peng/articles/13345307.html

https://github.com/golang/mock

https://geektutu.com/post/quick-gomock.html

https://yangxikun.com/golang/2021/06/19/golang-mock.html

updatedupdated2023-03-022023-03-02