Golang单元测试中如何模拟方法调用
Golang单元测试中如何模拟方法调用
我编写了一个名为 NewSchema(ctx contractapi.TransactionContextInterface, groupName string, schemaName string, schemaText string) 的函数,它会调用另一个名为 SubmittingIdentityHasOU(ctx, 'random_string') 的函数。我正在为 NewSchema 函数编写单元测试。
我一直在尝试模拟 SubmittingIdentityHasOU 的响应,以便根据我的测试用例返回 true 或 false。然而,似乎每次我尝试时,测试并没有调用该函数的模拟版本,而是调用了主代码中的实际实现,这显然会导致很多问题。
我想学习如何模拟嵌入函数的行为,以便能够轻松测试 NewSchema 函数的各种情况。
更多关于Golang单元测试中如何模拟方法调用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang单元测试中如何模拟方法调用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Golang单元测试中模拟方法调用,可以使用接口和依赖注入的方式。以下是一个完整的示例:
// 定义接口
type IdentityChecker interface {
SubmittingIdentityHasOU(ctx contractapi.TransactionContextInterface, ou string) (bool, error)
}
// 修改你的结构体,使其包含接口
type SchemaService struct {
checker IdentityChecker
}
// 构造函数
func NewSchemaService(checker IdentityChecker) *SchemaService {
return &SchemaService{checker: checker}
}
// 修改你的函数,使用接口方法
func (s *SchemaService) NewSchema(ctx contractapi.TransactionContextInterface, groupName string, schemaName string, schemaText string) error {
hasOU, err := s.checker.SubmittingIdentityHasOU(ctx, "random_string")
if err != nil {
return err
}
if !hasOU {
return errors.New("identity does not have required OU")
}
// 其他业务逻辑
return nil
}
// 测试代码
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// 创建mock结构体
type MockIdentityChecker struct {
mock.Mock
}
func (m *MockIdentityChecker) SubmittingIdentityHasOU(ctx contractapi.TransactionContextInterface, ou string) (bool, error) {
args := m.Called(ctx, ou)
return args.Bool(0), args.Error(1)
}
func TestNewSchema_Success(t *testing.T) {
// 创建mock实例
mockChecker := new(MockIdentityChecker)
// 设置期望的行为
mockChecker.On("SubmittingIdentityHasOU", mock.Anything, "random_string").
Return(true, nil)
// 创建服务实例,注入mock
service := NewSchemaService(mockChecker)
// 调用被测试的函数
err := service.NewSchema(nil, "group1", "schema1", "schema text")
// 验证结果
assert.NoError(t, err)
mockChecker.AssertExpectations(t)
}
func TestNewSchema_Failure(t *testing.T) {
mockChecker := new(MockIdentityChecker)
// 模拟返回false的情况
mockChecker.On("SubmittingIdentityHasOU", mock.Anything, "random_string").
Return(false, nil)
service := NewSchemaService(mockChecker)
err := service.NewSchema(nil, "group1", "schema1", "schema text")
assert.Error(t, err)
assert.Equal(t, "identity does not have required OU", err.Error())
mockChecker.AssertExpectations(t)
}
如果你不能修改现有结构体,可以使用包装器模式:
// 包装器模式
var identityChecker = struct {
SubmittingIdentityHasOU func(ctx contractapi.TransactionContextInterface, ou string) (bool, error)
}{
SubmittingIdentityHasOU: func(ctx contractapi.TransactionContextInterface, ou string) (bool, error) {
// 实际实现
return realSubmittingIdentityHasOU(ctx, ou)
},
}
// 在测试中替换实现
func TestNewSchema_WithWrapper(t *testing.T) {
// 保存原始函数
originalFunc := identityChecker.SubmittingIdentityHasOU
// 测试完成后恢复
defer func() {
identityChecker.SubmittingIdentityHasOU = originalFunc
}()
// 设置模拟实现
identityChecker.SubmittingIdentityHasOU = func(ctx contractapi.TransactionContextInterface, ou string) (bool, error) {
return true, nil
}
// 调用你的NewSchema函数
// 现在它会使用模拟的实现
}
对于使用testify/mock的更复杂场景:
// 使用gomock的示例
import (
"github.com/golang/mock/gomock"
)
func TestNewSchema_WithGoMock(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockChecker := NewMockIdentityChecker(ctrl)
// 设置精确的期望
mockChecker.EXPECT().
SubmittingIdentityHasOU(gomock.Any(), "random_string").
Return(true, nil).
Times(1)
service := NewSchemaService(mockChecker)
err := service.NewSchema(nil, "group1", "schema1", "schema text")
assert.NoError(t, err)
}
关键点:
- 通过接口实现依赖注入
- 在测试中创建实现了接口的mock结构体
- 使用
testify/mock或gomock来设置期望的行为 - 验证mock的调用是否符合预期

