Golang单元测试中如何模拟方法调用

Golang单元测试中如何模拟方法调用 我编写了一个名为 NewSchema(ctx contractapi.TransactionContextInterface, groupName string, schemaName string, schemaText string) 的函数,它会调用另一个名为 SubmittingIdentityHasOU(ctx, 'random_string') 的函数。我正在为 NewSchema 函数编写单元测试。

我一直在尝试模拟 SubmittingIdentityHasOU 的响应,以便根据我的测试用例返回 truefalse。然而,似乎每次我尝试时,测试并没有调用该函数的模拟版本,而是调用了主代码中的实际实现,这显然会导致很多问题。

我想学习如何模拟嵌入函数的行为,以便能够轻松测试 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)
}

关键点:

  1. 通过接口实现依赖注入
  2. 在测试中创建实现了接口的mock结构体
  3. 使用testify/mockgomock来设置期望的行为
  4. 验证mock的调用是否符合预期
回到顶部