前言

go test 上篇 给大家介绍了golang自带的测试框架,包括单元测试和性能测试。但是在实际生产中测试经常会遇到一些网络或者依赖的第三方系统接口,运行测试用例的时候希望忽略这些接口的实际依赖,聚焦在具体业务逻辑代码,这就需要模拟这些接口的行为,也就是我今天介绍给大家的golang/mock,一个golang的mock框架。

演示环境

$ uname -a
Darwin 18.6. Darwin Kernel Version 18.6.: Thu Apr :: PDT ; root:xnu-4903.261.~/RELEASE_X86_64 x86_64
$ go version
go version go1.12.4 darwin/amd64

安装

go get github.com/golang/mock/gomock
go install github.com/golang/mock/mockgen

用法

1.定义我们需要mock的接口。如:

type MyInterface interface {
SomeMethod(x int64, y string)
}

2.使用mockgen命令生成接口的mock文件。

mockgen -package example_test -destination example_mock.go

3.在测试中使用mock接口:

 func TestMyThing(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish() mockObj := something.NewMockMyInterface(mockCtrl)
mockObj.EXPECT().SomeMethod(, "blah")
// pass mockObj to a real object and play with it.
}

接口说明

以官方提供的https://github.com/golang/mock/blob/master/sample/user_test.go文件作为示例说明:

 func TestRemember(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish() mockIndex := mock_user.NewMockIndex(ctrl)
mockIndex.EXPECT().Put("a", ) // literals work
mockIndex.EXPECT().Put("b", gomock.Eq()) // matchers work too // NillableRet returns error. Not declaring it should result in a nil return.
mockIndex.EXPECT().NillableRet()
// Calls that returns something assignable to the return type.
boolc := make(chan bool)
// In this case, "chan bool" is assignable to "chan<- bool".
mockIndex.EXPECT().ConcreteRet().Return(boolc)
// In this case, nil is assignable to "chan<- bool".
mockIndex.EXPECT().ConcreteRet().Return(nil) // Should be able to place expectations on variadic methods.
mockIndex.EXPECT().Ellip("%d", , , , , ) // direct args
tri := []interface{}{, , , , }
mockIndex.EXPECT().Ellip("%d", tri...) // args from slice
mockIndex.EXPECT().EllipOnly(gomock.Eq("arg")) user.Remember(mockIndex, []string{"a", "b"}, []interface{}{, })
// Check the ConcreteRet calls.
if c := mockIndex.ConcreteRet(); c != boolc {
t.Errorf("ConcreteRet: got %v, want %v", c, boolc)
}
if c := mockIndex.ConcreteRet(); c != nil {
t.Errorf("ConcreteRet: got %v, want nil", c)
} // Try one with an action.
calledString := ""
mockIndex.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(key string, _ interface{}) {
calledString = key
})
mockIndex.EXPECT().NillableRet()
user.Remember(mockIndex, []string{"blah"}, []interface{}{})
if calledString != "blah" {
t.Fatalf(`Uh oh. %q != "blah"`, calledString)
} // Use Do with a nil arg.
mockIndex.EXPECT().Put("nil-key", gomock.Any()).Do(func(key string, value interface{}) {
if value != nil {
t.Errorf("Put did not pass through nil; got %v", value)
}
})
mockIndex.EXPECT().NillableRet()
user.Remember(mockIndex, []string{"nil-key"}, []interface{}{nil})
}
  • EXPECT 表示期望在后续的测试代码中会用到,且一定要用到,否则会报错。例如第6行的Put("a", 1)方法会在第24行的Remeber函数里面调用。
  • Return 表示mock接口的返回值,例如第14行的ConcreteRet()函数返回boolc。
  • Do 表示当匹配到对应的函数时执行对应的行为。例如第35行,当匹配到put(gomock.Any(), gomock.Any())时执行func(key string, _ interface{}),如果函数需要返回值用DoAndReturn函数。
  • Any 表示构造一个一直会match的matcher。

上述示例使用了Index接口的mock方法。第6,7,10,14,16,19,21,22定义的EXPECT行为会在第24行的Remeber函数中被调用:

user.Remember(mockIndex, []string{"a", "b"}, []interface{}{, })
 func Remember(index Index, keys []string, values []interface{}) {
for i, k := range keys {
index.Put(k, values[i])
}
err := index.NillableRet()
if err != nil {
log.Fatalf("Woah! %v", err)
}
if len(keys) > && keys[] == "a" {
index.Ellip("%d", , , , , )
index.Ellip("%d", , , , , )
index.EllipOnly("arg")
}
}

mock接口文件完成后运行测试:

$ git clone https://github.com/golang/mock
Cloning into 'mock'...
remote: Enumerating objects: , done.
remote: Counting objects: % (/), done.
remote: Compressing objects: % (/), done.
remote: Total (delta ), reused (delta ), pack-reused
Receiving objects: % (/), 450.07 KiB | 354.00 KiB/s, done.
Resolving deltas: % (/), done.
$ cd mock/sample/
$ go test -v
=== RUN TestRemember
--- PASS: TestRemember (.00s)
=== RUN TestVariadicFunction
--- PASS: TestVariadicFunction (.00s)
=== RUN TestGrabPointer
--- PASS: TestGrabPointer (.00s)
=== RUN TestEmbeddedInterface
--- PASS: TestEmbeddedInterface (.00s)
=== RUN TestExpectTrueNil
--- PASS: TestExpectTrueNil (.00s)
PASS
ok github.com/golang/mock/sample .017s

在实际生产中经常将需要mock的接口对象定义为一个全局变量,然后在测试用例中用mock对象替换这个对象,替换的方法可以直接替换,也可以用goStub第三方优雅替换。

 var configFile = "config.json"

 func GetConfig() ([]byte, error) {
return ioutil.ReadFile(configFile)
} // Test code
stubs := gostub.Stub(&configFile, "/tmp/test.config") data, err := GetConfig()
// data will now return contents of the /tmp/test.config file

总结

文章具体介绍了gomock库的使用场景和具体用法,作为go test官方测试框架的一个补充。gomock在生产代码中会被经常用到,当然也有其他的golang mock第三方开源库,例如testify。具体的选择需要根据大家的需求具体分析。


go test 下篇的更多相关文章

  1. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  2. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  3. LabVIEW 吸星大法 - 看见的好东西都是我的(下篇)

    前言 写了多年的LabVIEW程序,你是否面临这样的问题 总是在做一些重复的工作,感觉很没有意思: 总在不停的写代码,做类似的控件,实现相同的功能,丝毫没有成就感: 总在天加班,没有时间去提高自己; ...

  4. TaintDroid剖析之DVM变量级污点跟踪(下篇)

    TaintDroid剖析之DVM变量级污点跟踪(下篇)作者:简行.走位@阿里聚安全 ​ 1 回顾 在上一章节中我们详细分析了TaintDroid对DVM方法参数和方法变量的变量级污点跟踪机制,现在我们 ...

  5. ASP.NET Core的配置(4):多样性的配置来源[下篇]

    我们在上篇和中篇对配置模型中默认提供的各种ConfigurationProvider进行了深入详尽的介绍,如果它们依然不能满足项目中的配置需求,我们可以还可以通过自定义ConfigurationPro ...

  6. ASP.NET Core的配置(3): 将配置绑定为对象[下篇]

    我们在<读取配置信息>通过实例的形式演示了如何利用Options模型以依赖注入的方式直接获取由指定配置节绑定生成的Options对象,我们再次回顾一下当初我们编写的程序.如下面的代码片段所 ...

  7. 谈谈基于OAuth 2.0的第三方认证 [下篇]

    从安全的角度来讲,<中篇>介绍的Implicit类型的Authorization Grant存在这样的两个问题:其一,授权服务器没有对客户端应用进行认证,因为获取Access Token的 ...

  8. Sass-也许你想和CSS玩耍起来(下篇)

    问心无愧,共勉! sass-也许你想和CSS玩耍起来(上篇) 上篇中主要介绍了一些sass的基本特性.下篇中,主要是写一些我们常用的sass控制命令,函数和规则. sass进阶 控制命令 可能看过上篇 ...

  9. MS SQL统计信息浅析下篇

       MS SQL统计信息浅析上篇对SQL SERVER 数据库统计信息做了一个整体的介绍,随着我对数据库统计信息的不断认识.理解,于是有了MS SQL统计信息浅析下篇. 下面是我对SQL Serve ...

  10. 那些年我们写过的T-SQL(下篇)

    下篇的内容很多都会在工作中用到,尤其是可编程对象,那些年我们写过的存储过程,有木有?到目前为止很多大型传统企业仍然很依赖存储过程.这部分主要难理解的部分是事务和锁机制这块,本文会进行简单的阐述.虽然很 ...

随机推荐

  1. hdu 1576(逆元)

    A/B Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  2. Unity工程资源破解

        Unity工程资源提取其实还是很方便的,网上也有很多相关介绍,比如雨凇就专门写了一遍关于破解Unity资源的文章(http://www.xuanyusong.com/archives/3618 ...

  3. Jenkins配置git进行构建失败:Error cloning remote repo 'origin'的解决思路

    说明:这个没有实际的解决方法,只提供一个思路去解决. 操作系统:windows 背景:在配置的节点之后,由于是windows的系统,运行git克隆地址,使用的是SSH协议地址.出现如下的错误: Err ...

  4. Enter Query Mode Search Tricks Using Enter_Query Built-in in Oracle Forms

    In this post you will learn how to specify any condition in enter query mode of Oracle Forms. Whenev ...

  5. extern static const abstract virtual

    extern static const abstract virtual const const.常量,初始化过后值不能再变化的变量.

  6. stretchableImageWithLeftCapWidth气泡拉伸

    - (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCap ...

  7. netty实现长连接心跳检

    主要逻辑: 使用netty实现长连接,主要靠心跳来维持服务器端及客户端连接. 实现的逻辑主要是: 服务器端方面: 1, 服务器在网络空闲操作一定时间后,服务端失败心跳计数器加1. 2, 如果收到客户端 ...

  8. java查看工具jstack-windows

    Prints Java thread stack traces for a Java process, core file, or remote debug server. This command ...

  9. 【C语言学习】封装和模块化思想

    刚学习完C后,做的关于C的课程设计是在一个源文件里放了几百行代码,并且各个功能之间都是相互依赖的,这样就会非常麻烦. 由于当我要改动某个地方的时候,就会牵连着要改动喝多的地方.而在实际的程序设计中.这 ...

  10. Archlinux 下的 VMWare Workstation 维护笔记

    印象中 Archlinux 下的 VMWare Workstation 总是出问题, 因此写这个帖子, 记录出问题时间/原因/解决方案. PS: 每次更新内核后可能需要重新编译 vmware 的内核模 ...