# 测试规范 > Go 后端测试约定。 - 新逻辑配 table-driven 测试,用例含名字、输入、期望值;`go test ./...` 全绿才算完成。 - 断言失败时输出 `got/want` 对比,便于定位。 - 辅助函数标注 `t.Helper()`,清理动作用 `t.Cleanup()`。 ## 上传接口边界测试 - “文件最大 N 字节”与 HTTP 请求体上限不是同一个契约:multipart boundary、 part header 和文件名会额外占空间。路由级 body limit 必须为封装开销留余量, service 再对文件字节数执行精确上限。 - 新增或修改上传接口时,完整路由测试至少覆盖:恰好 N 字节成功、N+1 返回 413、空文件返回 400、伪造扩展名返回 415。 - 内容校验深度与威胁模型匹配:管理员向自己租户上传、由上游域名托管的低价值 资源,扩展名白名单 + 魔数嗅探即可,不引入完整解码/结构校验 (2026-07 IdP 图标曾过度实现后精简,见 idp-icon-upload.md)。 - 临时远端资源使用服务端随机名,测试相同客户端文件名不会复用上游对象;清理接口 只接受该功能生成的不可猜测标识,不能因共享 `images/` 前缀删除其他业务资产。 ## 多步远端写入测试 - `创建 A → 配置 B` 这类流程至少覆盖:创建失败、B 失败且 A 回滚成功、B 失败且 A 回滚失败/状态未知。只有确认回滚成功才能返回普通失败。 - 部分成功响应必须有稳定机器码和关联 ID,前端据此保留已被 A 引用的临时资源; 底层上游错误只进服务端日志,测试断言不会泄露到 HTTP body。 ## 常见坑:`:memory:` SQLite 每个连接是独立库 **症状**:被测代码里有异步 goroutine 写库(如日志异步落库)时,测试偶发 `no such table: xxx`。 **原因**:glebarez/sqlite 的 `:memory:` 数据库按连接隔离;GORM 连接池为新 goroutine 开新连接时,拿到的是一个没跑过 AutoMigrate 的空库。 **修复**:测试 helper 建库后锁单连接,生产文件库不受影响: ```go sqlDB, _ := db.DB() sqlDB.SetMaxOpenConns(1) // :memory: 每连接独立库,异步写复用同一连接 ``` **预防**:测试涉及「后台 goroutine 写库」时一律加此设置(参照 internal/api/router_test.go 与 internal/service 各测试 helper);异步写路径同时拆出同步内核函数(如 `record` / `Record`)供测试直调断言。