
本文介绍在 go 单元测试中,如何利用 redigomock 模拟并验证包含 multi、多个命令及 exec 的完整 redis 事务执行过程,涵盖命令链路断言、返回值预期与常见陷阱规避。
本文介绍在 go 单元测试中,如何利用 redigomock 模拟并验证包含 multi、多个命令及 exec 的完整 redis 事务执行过程,涵盖命令链路断言、返回值预期与常见陷阱规避。
在 Go 生态中,redigomock 是一个轻量但功能完备的 Redis 客户端(如 github.com/garyburd/redigo/redis)模拟库,广泛用于无依赖的单元测试。针对 Redis 的原子性事务(MULTI / EXEC 流程),redigomock 并未提供特殊封装,而是通过显式记录命令序列 + 精确匹配执行路径来实现可靠验证——关键在于理解其“命令队列+延迟执行”的设计逻辑。
✅ 正确测试 MULTI 事务的步骤
- 创建 mock 连接:调用 redigomock.NewConn() 获取可追踪的 mock 连接实例;
- 逐条注册事务命令:按实际执行顺序调用 connection.Command(),包括 MULTI、中间操作(如 SET、EXPIRE)和最终 EXEC;
- 为 EXEC 设置预期返回值:使用 .Expect(...) 明确声明 EXEC 应返回的响应切片(每个元素对应一条被包裹命令的执行结果);
- 验证各命令是否被调用:通过 connection.Stats(cmd) 断言每条命令恰好被执行一次(返回值为 1 表示命中)。
以下是一个完整、可直接运行的测试示例(兼容 go-check):
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
func (s *MySuite) TestRedisTransaction(c *C) {
conn := redigomock.NewConn()
// 构建事务命令链
cmdMulti := conn.Command("MULTI")
cmdSet := conn.Command("SET", "person-123", "123456")
cmdExpire := conn.Command("EXPIRE", "person-123", "1000")
cmdExec := conn.Command("EXEC").Expect([]interface{}{"OK", "1"}) // 注意:SET 返回 "OK",EXPIRE 返回 int(1)
// 执行业务逻辑(此处应调用含 redis.Do(...) 的函数)
// e.g., err := myService.SavePerson(ctx, conn, "123456")
// 断言每条命令均被准确触发一次
c.Check(conn.Stats(cmdMulti), Equals, 1)
c.Check(conn.Stats(cmdSet), Equals, 1)
c.Check(conn.Stats(cmdExpire), Equals, 1)
c.Check(conn.Stats(cmdExec), Equals, 1)
}
⚠️ 注意事项与最佳实践
- 命令顺序必须严格一致:MULTI 后的所有命令需按实际调用顺序注册,redigomock 会校验执行流完整性;
- EXEC 的 .Expect() 不可省略:若未设置预期返回值,EXEC 将默认返回 nil,导致业务逻辑误判;
- 避免拼写错误:如将 "EXPIRE" 误写为 "EXPIERS",旧版 redigomock 可能静默忽略(PR #21 已修复此问题,建议使用 v1.1.0+);
- 不支持嵌套事务:redigomock 仅模拟单层 MULTI/EXEC,DISCARD 或 WATCH 需单独覆盖测试用例;
- 类型安全提示:.Expect() 中的 []interface{} 应与真实 Redis 响应类型对齐(如 INCR 返回 int64,需写 int64(1) 或用 redis.Int64Reply 辅助转换)。
通过以上方式,你不仅能验证事务是否被发起,更能精准控制每一步的输入输出,大幅提升 Redis 相关逻辑的测试覆盖率与可靠性。










