如何为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
相关产品推荐
相关产品推荐

