go test 下篇

叁歲伎倆 2021-11-23 15:02 400阅读 0赞

前言

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

演示环境

  1. $ uname -a
  2. 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
  3. $ go version
  4. go version go1.12.4 darwin/amd64

#

安装

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

#

用法

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

  1. type MyInterface interface {
  2. SomeMethod(x int64, y string)
  3. }

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

  1. mockgen -package example_test -destination example_mock.go

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

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

接口说明

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

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

  1. user.Remember(mockIndex, []string{
  2. "a", "b"}, []interface{}{
  3. 1, 2})
  4. 1 func Remember(index Index, keys []string, values []interface{}) {
  5. 2 for i, k := range keys {
  6. 3 index.Put(k, values[i])
  7. 4 }
  8. 5 err := index.NillableRet()
  9. 6 if err != nil {
  10. 7 log.Fatalf("Woah! %v", err)
  11. 8 }
  12. 9 if len(keys) > 0 && keys[0] == "a" {
  13. 10 index.Ellip("%d", 0, 1, 1, 2, 3)
  14. 11 index.Ellip("%d", 1, 3, 6, 10, 15)
  15. 12 index.EllipOnly("arg")
  16. 13 }
  17. 14 }

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

  1. $ git clone https://github.com/golang/mock
  2. Cloning into 'mock'...
  3. remote: Enumerating objects: 4, done.
  4. remote: Counting objects: 100% (4/4), done.
  5. remote: Compressing objects: 100% (4/4), done.
  6. remote: Total 1568 (delta 0), reused 2 (delta 0), pack-reused 1564
  7. Receiving objects: 100% (1568/1568), 450.07 KiB | 354.00 KiB/s, done.
  8. Resolving deltas: 100% (807/807), done.
  9. $ cd mock/sample/
  10. $ go test -v
  11. === RUN TestRemember
  12. --- PASS: TestRemember (0.00s)
  13. === RUN TestVariadicFunction
  14. --- PASS: TestVariadicFunction (0.00s)
  15. === RUN TestGrabPointer
  16. --- PASS: TestGrabPointer (0.00s)
  17. === RUN TestEmbeddedInterface
  18. --- PASS: TestEmbeddedInterface (0.00s)
  19. === RUN TestExpectTrueNil
  20. --- PASS: TestExpectTrueNil (0.00s)
  21. PASS
  22. ok github.com/golang/mock/sample 0.017s

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

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

总结

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

转载于:https://www.cnblogs.com/makelu/p/11065628.html

发表评论

表情:
评论列表 (有 0 条评论,400人围观)

还没有评论,来说两句吧...

相关阅读

    相关 类型转换(下篇

    1. `-、、/、%`这四种都会把符号两边转成数字来进行运算 2. `+`由于不仅是数字运算符,还是字符串的连接符,所以分为两种情况: 两端都是数字则进行数字计算

    相关 go test 下篇

    前言 go test [上篇][Link 1] 给大家介绍了golang自带的测试框架,包括单元测试和性能测试。但是在实际生产中测试经常会遇到一些网络或者依赖的第三方系统