golang单元测试模拟框架插件gomock的使用
golang单元测试模拟框架插件gomock的使用
简介
gomock是Go编程语言的模拟框架。它与Go内置的testing
包很好地集成,但也可以在其他上下文中使用。
支持的Go版本
go.uber.org/mock支持官方Go发布策略支持的所有Go版本,即最近的2个Go版本。
安装
安装mockgen
工具:
go install go.uber.org/mock/mockgen@latest
验证安装是否成功:
mockgen -version
如果失败,请确保GOPATH/bin在你的PATH中:
export PATH=$PATH:$(go env GOPATH)/bin
运行mockgen
mockgen
有三种操作模式:archive、source和package。
Archive模式
从包存档文件(.a)生成模拟接口:
# Build the package to a archive.
go build -o pkg.a database/sql/driver
mockgen -archive=pkg.a database/sql/driver Conn,Driver
Source模式
从源文件生成模拟接口:
mockgen -source=foo.go [other options]
Package模式
通过指定包和接口名称工作:
mockgen database/sql/driver Conn,Driver
# Convenient for `go:generate`.
mockgen . Conn,Driver
完整示例
定义接口
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) {
// ...
}
构建Mock测试
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
m := NewMockFoo(ctrl)
// Asserts that the first and only call to Bar() is passed 99.
// Anything else will fail.
m.
EXPECT().
Bar(gomock.Eq(99)).
Return(101)
SUT(m)
}
构建Stub测试
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
m := NewMockFoo(ctrl)
// Does not make any assertions. Executes the anonymous functions and returns
// its result when Bar is invoked with 99.
m.
EXPECT().
Bar(gomock.Eq(99)).
DoAndReturn(func(_ int) int {
time.Sleep(1*time.Second)
return 101
}).
AnyTimes()
// Does not make any assertions. Returns 103 when Bar is invoked with 101.
m.
EXPECT().
Bar(gomock.Eq(101)).
Return(103).
AnyTimes()
SUT(m)
}
修改失败消息
修改Want值
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
)
修改Got值
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i any) string {
// Leading 0s
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
)
更多关于golang单元测试模拟框架插件gomock的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang单元测试模拟框架插件gomock的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang单元测试模拟框架gomock使用指南
什么是gomock
gomock是Go语言官方提供的mock框架,用于在单元测试中创建和管理mock对象。它通常与mockgen
工具配合使用,可以基于接口自动生成mock实现。
安装gomock
首先需要安装gomock和mockgen工具:
go install github.com/golang/mock/mockgen@latest
go get github.com/golang/mock/gomock
基本使用流程
1. 定义接口
假设我们有一个简单的接口:
// user.go
package user
type UserRepository interface {
GetUser(id int) (*User, error)
SaveUser(user *User) error
}
type User struct {
ID int
Name string
}
2. 生成mock代码
使用mockgen生成mock实现:
mockgen -source=user.go -destination=mock_user.go -package=user
这会生成一个mock_user.go
文件,包含MockUserRepository
结构体。
3. 编写测试用例
// user_test.go
package user
import (
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestGetUser(t *testing.T) {
// 创建gomock控制器
ctrl := gomock.NewController(t)
defer ctrl.Finish() // 断言mock方法被调用
// 创建mock对象
mockRepo := NewMockUserRepository(ctrl)
// 设置预期行为
expectedUser := &User{ID: 1, Name: "John"}
mockRepo.EXPECT().
GetUser(1). // 预期调用参数
Return(expectedUser, nil). // 预期返回值
Times(1) // 预期调用次数
// 注入mock对象并测试
service := UserService{Repo: mockRepo}
user, err := service.GetUser(1)
// 验证结果
assert.NoError(t, err)
assert.Equal(t, expectedUser, user)
}
gomock常用功能
1. 参数匹配
gomock提供了多种参数匹配方式:
// 精确匹配
mockRepo.EXPECT().GetUser(1).Return(user, nil)
// 任意参数
mockRepo.EXPECT().GetUser(gomock.Any()).Return(user, nil)
// 自定义匹配器
mockRepo.EXPECT().GetUser(gomock.Eq(1)).Return(user, nil)
2. 调用次数控制
// 精确调用次数
mockRepo.EXPECT().GetUser(1).Return(user, nil).Times(2)
// 至少调用n次
mockRepo.EXPECT().GetUser(1).Return(user, nil).MinTimes(1)
// 最多调用n次
mockRepo.EXPECT().GetUser(1).Return(user, nil).MaxTimes(3)
// 任意次数(0或多次)
mockRepo.EXPECT().GetUser(1).Return(user, nil).AnyTimes()
3. 调用顺序控制
gomock.InOrder(
mockRepo.EXPECT().GetUser(1).Return(user1, nil),
mockRepo.EXPECT().GetUser(2).Return(user2, nil),
)
4. 动态返回值
mockRepo.EXPECT().GetUser(gomock.Any()).
DoAndReturn(func(id int) (*User, error) {
return &User{ID: id, Name: "Dynamic"}, nil
})
5. 副作用操作
mockRepo.EXPECT().SaveUser(gomock.Any()).
Do(func(user *User) {
t.Log("User saved:", user)
}).
Return(nil)
最佳实践
- 每个测试用例只测试一个功能点:保持测试简单明确
- 合理设置预期:不要过度mock,只mock必要的依赖
- 验证调用次数:确保依赖被正确调用
- 清理资源:使用
defer ctrl.Finish()
确保mock验证被执行 - 结合表格驱动测试:对于多组输入输出的测试更清晰
func TestGetUser(t *testing.T) {
tests := []struct {
name string
userID int
mockUser *User
mockErr error
wantErr bool
}{
{"success", 1, &User{ID: 1, Name: "John"}, nil, false},
{"not found", 2, nil, errors.New("not found"), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockRepo := NewMockUserRepository(ctrl)
mockRepo.EXPECT().
GetUser(tt.userID).
Return(tt.mockUser, tt.mockErr)
service := UserService{Repo: mockRepo}
user, err := service.GetUser(tt.userID)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.mockUser, user)
}
})
}
}
gomock是Go单元测试中非常强大的工具,合理使用可以大大提高测试的可靠性和可维护性。