You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为UserService.go的GetListOfUsers()编写单元测试?模拟aerospike.Recordset遇阻

构造模拟aerospike.Recordset用于单元测试

问题场景

要为UserService.GetListOfUsers()编写单元测试,需要mockUsersRepository.QueryDataEquality()方法,使其返回一个模拟的aerospike.Recordset对象,但不清楚如何构造该对象。

解决方案

方法一:手动构造Recordset

aerospike.Recordset的核心是Results()方法,它返回一个chan *aerospike.Result。我们可以直接构造这个通道,并初始化Recordset对象返回该通道。

构造模拟Recordset的工具函数

import (
    "encoding/json"
    "your-project-path/models"
    aero "github.com/aerospike/aerospike-client-go"
)

// createMockRecordset 根据传入的用户列表生成模拟的Recordset
func createMockRecordset(users []models.Users) *aero.Recordset {
    resultChan := make(chan *aero.Result)
    
    go func() {
        defer close(resultChan) // 确保通道关闭,避免测试阻塞
        
        for _, user := range users {
            // 将User对象转换为aerospike.Record需要的Bins格式(map[string]interface{})
            binBytes, err := json.Marshal(user)
            if err != nil {
                resultChan <- &aero.Result{Err: err}
                return
            }
            
            var bins map[string]interface{}
            if err := json.Unmarshal(binBytes, &bins); err != nil {
                resultChan <- &aero.Result{Err: err}
                return
            }
            
            // 发送模拟的Result到通道
            resultChan <- &aero.Result{
                Record: &aero.Record{
                    Bins: bins,
                },
            }
        }
    }()
    
    // 初始化Recordset,返回我们构造的结果通道
    return &aero.Recordset{
        Results: func() chan *aero.Result {
            return resultChan
        },
    }
}

在测试中使用

func (suite *UserServiceTestSuite) TestGetListOfUsers() {
    // 准备测试用的用户数据
    testUsers := []models.Users{
        {ID: "user_001", Name: "Alice", TeamID: "test_team"},
        {ID: "user_002", Name: "Bob", TeamID: "test_team"},
    }
    
    // 创建模拟的Recordset
    mockRecordSet := createMockRecordset(testUsers)
    
    // 设置mock仓库的期望
    suite.mockUserRepository.EXPECT().
        QueryDataEquality(models.UserSet, "t_id", "test_team").
        Return(mockRecordSet, nil)
    
    // 调用待测试方法
    userList, err := suite.userService.GetListOfUsers(suite.context, "test_team")
    
    // 断言结果
    suite.NoError(err)
    suite.Len(userList, 2)
    suite.Equal(testUsers[0].Name, userList[0].Name)
}

方法二:使用Mock库模拟Recordset接口

如果需要更灵活地控制Recordset的行为(比如测试关闭、错误场景),可以用testify/mock库模拟Recordset的接口。

定义MockRecordset结构体

import (
    aero "github.com/aerospike/aerospike-client-go"
    "github.com/stretchr/testify/mock"
)

type MockRecordset struct {
    mock.Mock
}

// 实现Recordset的Results方法
func (m *MockRecordset) Results() chan *aero.Result {
    args := m.Called()
    return args.Get(0).(chan *aero.Result)
}

// 实现Recordset的Close方法(如果你的代码中调用了Close)
func (m *MockRecordset) Close() error {
    args := m.Called()
    return args.Error(0)
}

在测试中使用

import "fmt"

func (suite *UserServiceTestSuite) TestGetListOfUsers_WithError() {
    // 构造带错误的结果通道
    resultChan := make(chan *aero.Result)
    go func() {
        defer close(resultChan)
        // 模拟查询返回错误
        resultChan <- &aero.Result{Err: fmt.Errorf("aerospike query error")}
    }()
    
    // 创建MockRecordset并设置期望
    mockRecordSet := new(MockRecordset)
    mockRecordSet.On("Results").Return(resultChan)
    
    // 设置mock仓库的期望
    suite.mockUserRepository.EXPECT().
        QueryDataEquality(models.UserSet, "t_id", "test_team").
        Return(mockRecordSet, nil)
    
    // 调用待测试方法
    userList, err := suite.userService.GetListOfUsers(suite.context, "test_team")
    
    // 断言错误
    suite.Error(err)
    suite.Nil(userList)
    // 验证mock的方法调用
    mockRecordSet.AssertExpectations(suite.T())
}

注意事项

  • 务必确保结果通道被正确关闭,否则测试可能会陷入无限阻塞。
  • 测试错误场景时,要构造包含错误的aerospike.Result发送到通道中,覆盖代码中的错误处理逻辑。

内容的提问来源于stack exchange,提问作者A_Gour

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:20:06