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 }
上述示例使用了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} -->
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有