文章目录
- func TestGetUser(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Go≥1.14可省略 // 创建UserRepository的Mock对象 mockRepo := mocks.NewMockUserRepository(ctrl) // 预设行为:当传入id=1时返回模拟用户 mockRepo.EXPECT(). GetUser(gomock.Eq(int64(1))). // 参数匹配 Return(&User{ID: 1, Name: “Alice”}, nil). // 返回值 Times(1) // 预期调用次数 // 注入Mock对象 service := NewUserService(mockRepo) user, err := service.GetUser(1) // 断言结果 assert.NoError(t, err) assert.Equal(t, “Alice”, user.Name) }
- 参数匹配器: mockRepo.EXPECT().GetUser(gomock.Any()).Return(nil, errors.New(“not found”)) // 匹配任意参数 动态返回值: mockRepo.EXPECT().GetUser(gomock.Any()). DoAndReturn(func(id int64) (*User, error) { if id == 1 { return &User{Name: “Alice”}, nil } return nil, errors.New(“not found”) }) 调用顺序控制: gomock.InOrder( mockRepo.EXPECT().GetUser(1).Return(user1, nil), mockRepo.EXPECT().GetUser(2).Return(user2, nil), )
目录
- 一、环境配置
- 二、面向接口编程(使用前提)
- 三、生成Mock代码
- 四、编写单元测试
- 1. 基础Mock使用
- 2. 高级功能
- 五、提升效率的技巧
- 六、适用场景
- 注意事项
安装gomock和mockgen工具
Go版本 <1.16:
GO111MODULE=on go get github.com/golang/mock/mockgen@v1.6.0
Go版本 ≥1.16:
go install github.com/golang/mock/mockgen@v1.6.0
安装后确保$GOPATH/bin在系统PATH中。
关键点:被模拟的依赖需抽象为接口。例如数据库操作接口:
// user_repository.go
type UserRepository interface {
GetUser(id int64) (*User, error)
}
业务层通过依赖注入使用接口而非具体实现。
通过mockgen为接口生成Mock实现类:
# 源码模式(推荐) mockgen -source=./user_repository.go -destination=./mocks/mock_user_repo.go -package=mocks # 反射模式(无源码时) mockgen -destination=./mocks/mock_user_repo.go -package=mocks your_module/user_repository UserRepository
参数说明:
-source:接口定义文件路径-destination:Mock代码输出路径-package:Mock代码包名(默认为mock_前缀)
func TestGetUser(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish() // Go≥1.14可省略
// 创建UserRepository的Mock对象
mockRepo := mocks.NewMockUserRepository(ctrl)
// 预设行为:当传入id=1时返回模拟用户
mockRepo.EXPECT().
GetUser(gomock.Eq(int64(1))). // 参数匹配
Return(&User{ID: 1, Name: "Alice"}, nil). // 返回值
Times(1) // 预期调用次数
// 注入Mock对象
service := NewUserService(mockRepo)
user, err := service.GetUser(1)
// 断言结果
assert.NoError(t, err)
assert.Equal(t, "Alice", user.Name)
}
参数匹配器:
mockRepo.EXPECT().GetUser(gomock.Any()).Return(nil, errors.New("not found")) // 匹配任意参数
动态返回值:
mockRepo.EXPECT().GetUser(gomock.Any()).
DoAndReturn(func(id int64) (*User, error) {
if id == 1 {
return &User{Name: "Alice"}, nil
}
return nil, errors.New("not found")
})
调用顺序控制:
gomock.InOrder(
mockRepo.EXPECT().GetUser(1).Return(user1, nil),
mockRepo.EXPECT().GetUser(2).Return(user2, nil),
)
自动化生成Mock
在接口文件中添加go:generate指令,一键生成Mock代码:
//go:generate mockgen -destination=./mocks/mock_user_repo.go -package=mocks your_module/user_repository UserRepository
执行go generate ./...自动更新所有Mock文件。
集成到CI流程
在CI脚本中加入测试命令,确保每次提交都运行单元测试:
# GitHub Actions示例
jobs:
test:
steps:
- run: go generate ./...
- run: go test -coverprofile=coverage.out ./...
结合表驱动测试
用多组输入验证同一逻辑,减少重复代码:
tests := []struct {
name string
id int64
want *User
}{
{"valid", 1, &User{Name: "Alice"}},
{"invalid", 2, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 配置Mock并调用被测方法
})
}
- 外部依赖隔离:数据库、API请求、文件IO等。
- 未实现的服务:依赖模块未完成时模拟其行为。
- 复杂依赖构造:避免为测试构造复杂数据。
- 避免过度Mock:仅Mock外部依赖,核心逻辑应直接测试。
- 更新Mock:接口变更后需重新生成Mock代码。
- 结合覆盖率分析:通过
go test -cover检查测试覆盖盲区。
go test -cover检查测试覆盖盲区。通过上述步骤,mockgen能大幅降低单元测试的编写复杂度,尤其适合依赖外部资源的场景,让测试更专注、更快速。
以上就是Go项目中使用mockgen提升单元测试效率的实践指南的详细内容,更多关于Go mockgen提升单元测试效率的资料请关注风君子博客其它相关文章!
您可能感兴趣的文章:
- golang使用go test输出单元测试覆盖率的方式
- Go语言单元测试的实现及用例
- 在go中进行单元测试的案例分享
- Go语言:打造优雅数据库单元测试的实战指南
- 详解在Go语言单元测试中如何解决文件依赖问题