前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go test 下篇

go test 下篇

作者头像
用户2937493
发布2019-09-10 17:53:29
4970
发布2019-09-10 17:53:29
举报
文章被收录于专栏:米奇爱编程米奇爱编程

前言

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

演示环境

$ uname -a
Darwin 18.6.0 Darwin Kernel Version 18.6.0: Thu Apr 25 23:16:27 PDT 2019; root:xnu-4903.261.4~2/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(4, "blah")
        // pass mockObj to a real object and play with it.
}

接口说明

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

 1 func TestRemember(t *testing.T) {
 2     ctrl := gomock.NewController(t)
 3     defer ctrl.Finish()
 4 
 5     mockIndex := mock_user.NewMockIndex(ctrl)
 6     mockIndex.EXPECT().Put("a", 1)            // literals work
 7     mockIndex.EXPECT().Put("b", gomock.Eq(2)) // matchers work too
 8 
 9     // NillableRet returns error. Not declaring it should result in a nil return.
10     mockIndex.EXPECT().NillableRet()
11     // Calls that returns something assignable to the return type.
12     boolc := make(chan bool)
13     // In this case, "chan bool" is assignable to "chan<- bool".
14     mockIndex.EXPECT().ConcreteRet().Return(boolc)
15     // In this case, nil is assignable to "chan<- bool".
16     mockIndex.EXPECT().ConcreteRet().Return(nil)
17 
18     // Should be able to place expectations on variadic methods.
19     mockIndex.EXPECT().Ellip("%d", 0, 1, 1, 2, 3) // direct args
20     tri := []interface{}{1, 3, 6, 10, 15}
21     mockIndex.EXPECT().Ellip("%d", tri...) // args from slice
22     mockIndex.EXPECT().EllipOnly(gomock.Eq("arg"))
23 
24     user.Remember(mockIndex, []string{"a", "b"}, []interface{}{1, 2})
25     // Check the ConcreteRet calls.
26     if c := mockIndex.ConcreteRet(); c != boolc {
27         t.Errorf("ConcreteRet: got %v, want %v", c, boolc)
28     }
29     if c := mockIndex.ConcreteRet(); c != nil {
30         t.Errorf("ConcreteRet: got %v, want nil", c)
31     }
32 
33     // Try one with an action.
34     calledString := ""
35     mockIndex.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(key string, _ interface{}) {
36         calledString = key
37     })
38     mockIndex.EXPECT().NillableRet()
39     user.Remember(mockIndex, []string{"blah"}, []interface{}{7})
40     if calledString != "blah" {
41         t.Fatalf(`Uh oh. %q != "blah"`, calledString)
42     }
43 
44     // Use Do with a nil arg.
45     mockIndex.EXPECT().Put("nil-key", gomock.Any()).Do(func(key string, value interface{}) {
46         if value != nil {
47             t.Errorf("Put did not pass through nil; got %v", value)
48         }
49     })
50     mockIndex.EXPECT().NillableRet()
51     user.Remember(mockIndex, []string{"nil-key"}, []interface{}{nil})
52 }
  • 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{}{1, 2})
 1 func Remember(index Index, keys []string, values []interface{}) {
 2     for i, k := range keys {
 3         index.Put(k, values[i])
 4     }
 5     err := index.NillableRet()
 6     if err != nil {
 7         log.Fatalf("Woah! %v", err)
 8     }
 9     if len(keys) > 0 && keys[0] == "a" {
10         index.Ellip("%d", 0, 1, 1, 2, 3)
11         index.Ellip("%d", 1, 3, 6, 10, 15)
12         index.EllipOnly("arg")
13     }
14 }

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

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

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

 1 var configFile = "config.json"
 2 
 3 func GetConfig() ([]byte, error) {
 4   return ioutil.ReadFile(configFile)
 5 }
 6 
 7 // Test code
 8 stubs := gostub.Stub(&configFile, "/tmp/test.config")
 9 
10 data, err := GetConfig()
11 // data will now return contents of the /tmp/test.config file

总结

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

<!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000} span.s1 {font-variant-ligatures: no-common-ligatures} span.Apple-tab-span {white-space:pre} --><!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000} span.s1 {font-variant-ligatures: no-common-ligatures} -->

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 演示环境
  • 安装
  • 用法
  • 接口说明
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档