zoukankan      html  css  js  c++  java
  • go test 下篇

    前言

    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。具体的选择需要根据大家的需求具体分析。

  • 相关阅读:
    linux mint使用起來比較穩定
    ubntu下的记录软件brasero ,mint内置
    [Enterprise Library]Data (二)DataCollection
    jquery 获取标签名
    谁会做logo呢
    供用淘宝卖家常用的称谓:各位亲,跨年,诸事顺利~
    rails缓存 学习
    test
    jquery 复习基础知识
    开机改用nginx启动,停止apache2的开机启动
  • 原文地址:https://www.cnblogs.com/makelu/p/11065628.html
Copyright © 2011-2022 走看看