Kratos单元测试不依赖真实数据库和Redis,核心是用enttest模拟SQLite内存实例并接口隔离依赖;enttest需启用mode=memory&cache=shared&_fk=1,Redis应mock redis.Cmdable接口而非硬连。

Kratos 项目单元测试不依赖真实数据库和 Redis,核心是用 enttest 模拟 Ent 框架的 SQLite 内存实例,再配合 gomock 或接口隔离来解耦外部依赖。硬连 MySQL/Redis 做测试,既慢又不稳定,也违背单元测试“快、隔离、可重复”的原则。
用 enttest 在内存中启动 Ent 测试实例
enttest.Open 是 Kratos 官方推荐的 Ent 单元测试入口,它在内存中拉起一个 SQLite 实例,不写磁盘、不占端口、秒启秒停。关键点在于驱动字符串必须带 mode=memory&cache=shared&_fk=1,否则外键约束不生效、事务行为异常。
- SQLite 内存模式只对单个连接有效;加
cache=shared才能让多个ent.Client共享同一内存 DB - 务必关闭
_fk=1(启用外键),否则ent.Schema.Create可能静默跳过约束定义 - 不要用
file::memory:—— 这是每个连接独立内存空间,表结构无法复用,测试会报 “no such table” - 如果业务强依赖 MySQL 特性(如 JSON 字段、窗口函数),那就不适合用 enttest,应改用
dockertest启容器做集成测试
如何 mock Redis 客户端避免真实连接
直接 new redis.NewClient 会尝试连 localhost:6379,CI 环境没 Redis 就挂。正确做法是把 redis.Cmdable 接口作为依赖传入 Data 结构体,测试时传入 gomock 生成的 mock 对象,或更轻量的 redis.UniversalClient + redis.NewMockClient(需 v8.11.5+)。
- 别在
setupTestData里硬写redis.NewClient(&redis.Options{Addr: "localhost:6379"}) - mock 的重点是覆盖
Get/Set/Del等常用方法,返回预设值即可验证逻辑分支 - 若使用
redis.UniversalClient,注意它内部会根据 URL 自动选择 Client 类型,mock 时需保持类型一致
测试用例里为什么总 panic “context deadline exceeded”
这是最常见的超时错误,根源不在业务代码,而在测试 setup 阶段:Ent 初始化 schema、Redis 连接握手、甚至日志初始化都可能隐式等待上下文。默认 context.Background() 没 deadline,但某些中间件(比如 Kratos 的 log 或 tracing)会在无显式 context 时 fallback 到带 timeout 的 context。
- 所有测试函数里,
ctx必须显式创建:ctx, cancel := context.WithTimeout(context.Background(), time.Second*5),并在defer cancel() - 检查
NewUserRepo构造时是否传了 logger ——log.NewStdLogger(os.Stdout)是安全的,但若用了log.With包裹 trace ID,就可能触发链路初始化阻塞 - Ent 的
client.Schema.Create默认不带 context,若你手动调了,必须传非空 ctx
真正难调试的不是语法或流程,而是测试环境和运行时环境的隐式差异:SQLite 的 autoincrement 行为 vs MySQL、Redis mock 的 pipeline 响应顺序、Ent hook 的执行时机……这些不会报错,但会让断言失败得莫名其妙。每次 assert 失败,先确认你 mock 的返回值和 enttest 的初始状态是否与生产逻辑对齐,而不是急着改业务代码。











