enttest.open 用于在内存中启动 sqlite 测试数据库,需配 sqlite3 驱动和 _fk=1 参数,配合 defer cleanup() 关闭 client,传入非 nil logger,并确保测试数据符合校验规则。

用 enttest.Open 搭建内存数据库测试环境
Kratos 项目里 data 层依赖数据库,但单元测试不能每次连真实 MySQL,enttest.Open 是 Ent 官方提供的轻量方案,直接在内存中启动 SQLite 实例,避免环境依赖和清理成本。
常见错误是误用 MySQL 驱动写内存连接字符串,比如 "file:ent?mode=memory" 对 MySQL 无效,必须配 SQLite 驱动(_ "github.com/mattn/go-sqlite3");另外 SQLite 不支持外键约束默认开启,得加 &_fk=1 参数,否则 enttest 初始化会报错。
- SQLite 内存模式只在单个测试函数生命周期内有效,无需手动删表或重置
- 若需测试 MySQL 特有行为(如 JSON 字段、事务隔离级别),必须起真实容器,此时
enttest.Open仅作连接池初始化,不模拟 schema - 记得在
setupTestData里统一 closeclient和rdb,否则测试并发时可能泄漏连接
测试函数里别漏掉 defercleanup()
每个测试函数开头调用 setupTestData(t) 后,必须紧跟 defercleanup()。这不是可选项——它负责关闭 Ent client 和 Redis client。漏掉会导致后续测试因端口占用、连接耗尽或 SQLite 文件锁失败而随机挂掉。
容易踩的坑是把 cleanup 放在 assert 后面,或者误写成 defer cleanup()(少了个括号变成立即执行)。正确写法只有这一种:defercleanup(),其中 cleanup 是函数值,不是调用。
- Redis client 若没 close,
go test进程退出前会卡住几秒等待连接超时 - Ent client 不 close 可能导致
sqlite3.ErrClosed在下个测试里突然抛出 - Windows 下 SQLite 内存 DB 的文件句柄未释放,还可能触发 “database is locked” 错误
NewUserRepo 构造时要传 logger,否则 panic
Kratos 的 Repo 通常依赖 log.Logger 做调试日志,比如 NewUserRepo(data, logger)。如果传 nil 或用空结构体代替,运行时大概率 panic:“invalid memory address or nil pointer dereference”,堆栈指向日志写入位置。
不用真的接 stdout 或文件,log.NewStdLogger(os.Stdout) 足够;更轻量的方案是用 log.NewNopLogger()(来自 github.com/go-kratos/kratos/v2/log),它完全静默,适合 CI 场景。
- 别用
log.Default(),它和 Kratos 日志系统不兼容,字段格式错乱 - 如果测试里需要捕获日志内容做断言(比如验证是否打了错误日志),得自己实现一个
log.Logger接口,把输出写进bytes.Buffer - logger 不影响业务逻辑,但缺失会导致测试无法跑通,属于“编译通过但运行必崩”的典型配置项
测试数据构造要避开 Mobile 字段长度校验
从 TestCreateUser 示例看,用 "13811211501" 模拟手机号没问题,但实际项目里 biz 层往往有长度或正则校验(比如 mobile: ^1[3-9]\d{9}$)。如果测试数据不符合,CreateUser 会提前返回 error,导致你误以为是 DB 层问题。
建议把测试用的 mobile、password 等字段值抽成常量,集中管理;或者用 github.com/google/uuid 生成随机字符串再截取,确保永远合法。密码字段尤其要注意:如果用了 bcrypt 加密,测试时得确认加密服务已注入,否则可能 panic 或返回空 hash。
- 别在测试里硬编码
"password",有些项目会对明文密码长度做校验,短密码直接拒绝 - 如果
User结构体里有非空约束字段(如ID、CreatedAt),Ent 自动生成的 schema 可能要求插入时提供值,得在测试构造时显式赋零值或忽略 - 最隐蔽的问题:SQLite 默认不校验 NOT NULL,而 MySQL 会,跨数据库测试时字段校验行为不一致,得靠 Ent migration 保证 schema 一致











