# testing-standards Specification ## Purpose TBD - created by archiving change optimize-test-db-connection. Update Purpose after archive. ## Requirements ### Requirement: 全局单例数据库连接 测试套件 **SHALL** 使用全局单例模式管理数据库连接,避免重复创建连接。 **技术约束**: - 使用 `sync.Once` 确保连接只初始化一次 - 整个测试套件(多个测试文件)共享同一个 `*gorm.DB` 实例 - `AutoMigrate` 只在首次连接时执行一次 - 连接失败应导致测试跳过,不应 panic #### Scenario: 多个测试共享连接 - **GIVEN** 测试套件包含 100+ 个测试用例 - **WHEN** 执行 `go test ./...` - **THEN** 只创建一次数据库连接 - **AND** 所有测试共享同一个连接池 - **AND** `AutoMigrate` 只执行一次 #### Scenario: 连接失败自动跳过 - **GIVEN** 测试数据库不可用 - **WHEN** 执行测试 - **THEN** 测试标记为 SKIP 而非 FAIL - **AND** 显示跳过原因: "无法连接测试数据库" --- ### Requirement: 全局单例 Redis 连接 测试套件 **SHALL** 使用全局单例模式管理 Redis 连接,避免重复创建连接。 **技术约束**: - 使用 `sync.Once` 确保连接只初始化一次 - 整个测试套件共享同一个 `*redis.Client` 实例 - 连接失败应导致测试跳过,不应 panic #### Scenario: 多个测试共享 Redis 连接 - **GIVEN** 测试套件包含 50+ 个需要 Redis 的测试 - **WHEN** 执行 `go test ./...` - **THEN** 只创建一次 Redis 连接 - **AND** 所有测试共享同一个 Redis 客户端 --- ### Requirement: 事务隔离 每个测试 **SHALL** 在独立事务中运行,并在测试结束后自动回滚,确保测试间完全隔离。 **技术约束**: - 使用 `db.Begin()` 开启事务 - 使用 `t.Cleanup(func() { tx.Rollback() })` 注册回滚函数 - 即使测试 panic 也能确保事务回滚(Go 的 defer/Cleanup 机制保证) - 事务隔离级别使用数据库默认值(PostgreSQL: READ COMMITTED) #### Scenario: 测试数据自动回滚 - **GIVEN** 测试 A 创建了用户 "test_user" - **WHEN** 测试 A 完成 - **THEN** 事务自动回滚 - **AND** 数据库中不存在 "test_user" - **AND** 测试 B 看不到测试 A 的数据 #### Scenario: 测试 panic 后自动清理 - **GIVEN** 测试 C 在执行中触发 panic - **WHEN** panic 发生 - **THEN** `t.Cleanup` 仍然执行 - **AND** 事务被回滚 - **AND** 数据库状态恢复到测试前 --- ### Requirement: Redis 键自动清理 每个测试 **SHALL** 使用测试名称作为 Redis 键前缀,并在测试结束后自动清理。 **技术约束**: - 键前缀格式: `test:{TestName}:*` - 使用 `t.Cleanup()` 注册清理函数 - 清理逻辑: `KEYS pattern` + `DEL keys...` - 支持嵌套测试(子测试继承父测试的前缀) #### Scenario: 测试前清理已有键 - **GIVEN** Redis 中存在键 `test:TestUserCreate:user:1` (上次运行残留) - **WHEN** 测试 `TestUserCreate` 开始 - **THEN** 清理所有匹配 `test:TestUserCreate:*` 的键 - **AND** Redis 处于干净状态 #### Scenario: 测试后自动清理 - **GIVEN** 测试 `TestUserLogin` 创建了键 `test:TestUserLogin:session:abc` - **WHEN** 测试完成 - **THEN** `t.Cleanup` 自动删除所有 `test:TestUserLogin:*` 键 - **AND** Redis 中不残留测试数据 --- ### Requirement: 向后兼容性 新的连接管理方案 **SHALL** 与现有的 `SetupTestDB`/`TeardownTestDB` 方案共存,支持渐进迁移。 **技术约束**: - 保留 `testutils/setup.go` 中的旧函数 - 在旧函数上添加 `// Deprecated` 注释 - 新旧方案可在同一测试套件中共存 - 迁移指引作为注释提供 #### Scenario: 旧测试正常运行 - **GIVEN** 测试文件使用 `SetupTestDB(t)` - **WHEN** 执行测试 - **THEN** 测试正常通过 - **AND** 不影响其他使用新方案的测试 #### Scenario: 新旧方案混用 - **GIVEN** 测试套件包含 50% 旧方案测试,50% 新方案测试 - **WHEN** 执行 `go test ./...` - **THEN** 所有测试正常运行 - **AND** 性能逐步提升(随迁移进度) --- ### Requirement: 简洁的测试代码 测试用例 **SHALL** 使用简洁的 API 创建事务和清理 Redis,减少样板代码。 **API 约束**: - `NewTestTransaction(t)` 返回事务,自动注册回滚 - `CleanTestRedisKeys(t)` 清理 Redis,自动注册清理函数 - 无需显式 `defer` 或手动清理 - 函数名清晰表达意图 #### Scenario: 最小化样板代码 - **GIVEN** 开发者编写新测试 - **WHEN** 使用新 API - **THEN** 只需 2 行代码完成设置: ```go tx := testutils.NewTestTransaction(t) testutils.CleanTestRedisKeys(t) ``` - **AND** 无需关心清理逻辑 #### Scenario: 对比旧方案 - **GIVEN** 旧方案需要 4 行代码: ```go db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) ``` - **WHEN** 使用新方案 - **THEN** 只需 2 行,且意图更清晰 --- ### Requirement: 性能优化 测试套件运行速度 **SHALL** 显著提升,通过减少连接创建和表结构检查次数。 **性能目标**: - 连接创建次数: 从 N(测试数量) 降低到 1 - AutoMigrate 次数: 从 N 降低到 1 - 测试套件总耗时提升: ≥ 5 倍 - 内存占用降低: ≥ 70% #### Scenario: 大型测试套件性能提升 - **GIVEN** 测试套件包含 200 个测试 - **WHEN** 全部迁移到新方案 - **THEN** 总耗时从 ~70 秒降低到 ~10 秒 - **AND** 性能提升约 7 倍 #### Scenario: 连接复用 - **GIVEN** 测试套件运行期间 - **WHEN** 监控数据库连接数 - **THEN** 最多保持 1 个连接(来自连接池) - **AND** 无重复连接创建 --- ### Requirement: 子测试事务行为 使用 `t.Run` 创建子测试时,**SHALL** 明确子测试与父事务的关系。 **技术约束**: - 父测试开启的事务,子测试默认共享 - 如需隔离,子测试必须开启独立事务 - 不支持在事务内使用 `t.Parallel()`(GORM 事务非线程安全) #### Scenario: 子测试共享父事务 - **GIVEN** 父测试开启事务 `tx := NewTestTransaction(t)` - **WHEN** 子测试使用 `t.Run` 运行 - **THEN** 子测试共享父事务 - **AND** 所有数据在父测试结束时统一回滚 #### Scenario: 子测试独立事务 - **GIVEN** 子测试需要数据隔离 - **WHEN** 子测试内调用 `tx := NewTestTransaction(t)` - **THEN** 子测试拥有独立事务 - **AND** 子测试结束时独立回滚 --- ### Requirement: Table-Driven Tests 支持 Table-Driven Tests **SHALL** 正确处理事务共享和回滚行为。 **技术约束**: - 父测试开启事务,所有 cases 共享 - 所有 cases 的数据在测试结束时统一回滚 - 如需 case 间隔离,每个 case 开启独立事务 #### Scenario: Cases 共享父事务 - **GIVEN** Table-Driven Test 有 5 个 test cases - **WHEN** 父测试开启事务 - **THEN** 所有 cases 在同一事务中运行 - **AND** Case 1 的数据对 Case 2 可见 - **AND** 所有数据在测试结束时统一回滚 #### Scenario: Cases 独立事务 - **GIVEN** 每个 case 需要独立数据环境 - **WHEN** 每个 case 内调用 `NewTestTransaction(t)` - **THEN** Cases 间完全隔离 - **AND** Case 1 的数据对 Case 2 不可见 --- ### Requirement: 规范文档化 测试连接管理规范 **SHALL** 以文档形式提供,并集成到项目开发规范中。 **文档要求**: - 路径: `docs/testing/test-connection-guide.md` - 包含: 原理说明、使用示例、最佳实践、常见陷阱 - 在 `AGENTS.md` 中引用,作为唯一标准 - 包含性能对比数据和迁移指南 #### Scenario: 开发者查找测试规范 - **GIVEN** 新加入的开发者需要编写测试 - **WHEN** 查阅 `AGENTS.md` 测试规范章节 - **THEN** 能找到 `test-connection-guide.md` 的引用 - **AND** 文档包含完整的 API 说明和示例 #### Scenario: 迁移指南 - **GIVEN** 现有测试使用旧的 `SetupTestDB` - **WHEN** 查阅迁移指南 - **THEN** 提供逐步迁移步骤 - **AND** 包含前后代码对比示例