使用 gomock 测试 Go 代码
gomock 是 Google 推出的用于 Go 的 mock 工具。它的大致用法是:
- 需要 mock 的地方,使用接口
- 执行 mockgen 生成代码
- 导入生成的代码,并且开始设置 mock 函数的行为
- 开始测试
安装
我们首先来看一下如何安装,如果 Go 的版本比较老,小于 1.16,那么就执行:
$ GO111MODULE=on go get github.com/golang/mock/[email protected]
$
如果大于,就执行:
$ go install github.com/golang/mock/[email protected]
$
使用
mockgen 有两种使用方式,第一种是 mockgen -source=foo.go
,指明从哪个源文件生成,第二种是
mockgen database/sql/driver Conn,Driver
,指明在哪个包的哪些接口。
此外,除了直接使用命令行,还可以写在 Makefile
里,或者是在 Go 源码里,以 go:generate
的方式写然后执行 go generate
。
案例
首先,我们来一个demo,加上 go:generate
来生成,用第一种方式:
package main
import (
"fmt"
)
//go:generate mockgen -source=./main.go -destination mock_main.go -package main
type Foo interface {
SayHi(sth string) error
}
type foo struct{}
func (f *foo) SayHi(sth string) error {
fmt.Printf("sth: %s\n", sth)
return nil
}
func main() {
f := foo{}
f.SayHi("hi foo")
}
生成代码:
$ go generate
$ ls
main.go mock_main.go trygomock
mockgen
还有其它参数,具体需要阅读一下 --help
:
$ mockgen --help
mockgen has two modes of operation: source and reflect.
...
接下来我们就可以使用 gomock 生成的方法来进行测试了,下列内容保存为 main_test.go
:
$ cat -n main_test.go
1 package main
2
3 import (
4 "testing"
5
6 "github.com/golang/mock/gomock"
7 )
8
9 func TestFoo(t *testing.T) {
10 ctrl := gomock.NewController(t) // 初始化 controller
11 defer ctrl.Finish()
12
13 mockFoo := NewMockFoo(ctrl) // 初始化 mock
14
15 mockFoo.EXPECT().SayHi(gomock.Any()).Return(nil) // 设置期望的入参和出参
16 if err := mockFoo.SayHi("haha"); err != nil { // 检查
17 t.Fatalf("bad return value: %s", err)
18 }
19
20 mockFoo.EXPECT().SayHi("nonono").Return(nil) // 同理,可以自动检查入参是否匹配
21 if err := mockFoo.SayHi("nonono"); err != nil {
22 t.Fatalf("bad return value: %s", err)
23 }
24
25 mockFoo.EXPECT().SayHi("nonono").Return(nil) // 这里不匹配,就会失败
26 if err := mockFoo.SayHi("haha"); err == nil {
27 t.Fatalf("bad return value: %s", err)
28 }
29 }
执行一下:
$ go test ./...
--- FAIL: TestFoo (0.00s)
main_test.go:26: Unexpected call to *main.MockFoo.SayHi([haha]) at /home/jiajun/Code/test/trygomock/main_test.go:26 because:
expected call at /home/jiajun/Code/test/trygomock/main_test.go:25 doesn't match the argument at index 0.
Got: haha (string)
Want: is equal to nonono (string)
expected call at /home/jiajun/Code/test/trygomock/main_test.go:15 has already been called the max number of times
expected call at /home/jiajun/Code/test/trygomock/main_test.go:20 doesn't match the argument at index 0.
Got: haha (string)
Want: is equal to nonono (string)
controller.go:269: missing call(s) to *main.MockFoo.SayHi(is equal to nonono (string)) /home/jiajun/Code/test/trygomock/main_test.go:25
controller.go:269: aborting test due to missing call(s)
FAIL
FAIL github.com/jiajunhuang/test/trygomock 0.001s
FAIL
可以看到,在26行调用的时候失败了。这就是一个 gomock 使用的简单例子。
gomock 使用方法概览
这一节,我们回顾一下使用方法:
- 首先要把需要 mock 的地方,写成接口
- 然后执行 mockgen 生成代码
- 在单元测试中,首先执行
ctrl := gomock.NewController(t)
然后defer ctrl.Finish()
- 使用
NewMockXXX
生成 mock 对象 - 调用
EXPECT()
方法,开始设置断言,其中参数,如果输入具体的类型,那么执行时就要传入具体的类型,如果输入gomock.Any()
的话,就会忽略参数类型,还有gomock.Eq
,gomock.Len
,gomock.All
- 可以通过
Return
设置返回结果 - 可以通过
Times
设置调用次数,还有AnyTimes
不限次数,MaxTimes
最多执行次数,MinTimes
最少执行次数 - 可以通过
After
设置执行顺序
总结
通过这篇文章我们学习了 gomock
的使用方法,gomock
在单元测试中,控制一些接口的表现以及返回结果都是非常有用的,希望
这篇文章能够对读者产生帮助。
更多文章
本站热门
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- frp 源码阅读与分析(一):流程和概念
- 用peewee代替SQLAlchemy
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(一):runc和containerd
- Docker组件介绍(二):shim, docker-init和docker-proxy
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程