Testing Guide
编写高质量测试的约束规范,确保测试真正验证行为而非仅仅覆盖代码。
Language
- Accept questions in both Chinese and English
- Always respond in Chinese
Trigger Conditions
- 用户正在编写测试代码
- 用户需要测试策略指导
- 用户提到测试覆盖率、断言、变异测试
- 用户要求分析代码分支覆盖情况
- Code Review 中涉及测试代码
- 关键词:"分支分析"、"branch analysis"、"未测试路径"、"覆盖盲区"
核心理念
测试的目的不是"覆盖代码",而是"验证行为"。
测试依据来自需求,不是来自代码。
⚠️ TDD 红阶段原则:断言必须来自 03-test-*.md 中对应 AC 的测试用例,禁止从实现代码反推测试。BCA(分支覆盖分析)仅用于防御性逻辑补充,不替代需求驱动测试。
测试质量金字塔
Level 3: 测试有效性 ─ 变异得分≥80%, 需求追溯100%
Level 2: 断言质量 ─ 禁止弱断言, 验证行为非实现
Level 1: 代码覆盖 ─ 行/分支覆盖≥80% (必要非充分)
Constraints
核心阈值表
全仓唯一权威测试质量阈值源。谓词语义与 /code-quality 核心阈值表 同构:≥ 达标值 = 合规;≥ 最低值且 < 达标值 = [Suggestion];< 最低值 = [Blocker]。镜像边界:agent 侧 references / SKILL 一律改指针不镜像(已迁移:dev-workflow verification-flow Phase 2、test-cases SKILL.md 覆盖率要求);仅用户侧产物模板(如 test-cases 的 test-cases / unit-test 模板,会复制进用户项目,届时本 skill 不在场)允许内联数字,且须标注"摘录自本表,变更需同 commit 同步"。
待迁移(本表的已知 agent 侧镜像,未清):
refactor/SKILL.md:216-220,250-252,271-272,281,359(测试状态分类以 ≥80% 定义"充分/不足",需连带重写状态机口径)、onboard/SKILL.md:247。
| 指标 | 达标值 | 最低值 | 适用 |
|------|-------:|-------:|------|
| 行覆盖率 | 80% | 60% | 全部项目 |
| 分支覆盖率 | 80% | 60% | 全部项目 |
| 变异得分 | 80% | 60% | 启用变异测试的项目;核心业务逻辑按达标值要求 |
| 每测试具体断言数 | ≥ 1 | — | 弱断言(toBeDefined/toBeTruthy/not.toBeNull)作为唯一断言 → [Blocker] |
单元测试约束
- [ ] 每个测试必须有 ≥1 个具体断言
- [ ] 禁止弱断言作为唯一断言 (
toBeDefined,toBeTruthy,not.toBeNull) - [ ] 测试名称必须描述预期行为——
test('works')这类名字在失败时不提供任何诊断信息 - [ ] 一个测试只验证一个行为——多个断言塞一个测试,失败时定位不到是哪条 AC 挂了
- [ ] 测试依据来自需求,不是代码
- [ ] 只 Mock 外部依赖,不 Mock 内部实现
覆盖率约束
- [ ] 行/分支覆盖率达标(阈值与分级见核心阈值表)
变异测试约束(推荐)
- [ ] 变异得分达标(阈值见核心阈值表;核心业务逻辑按达标值要求)
集成测试约束
- [ ] 必须验证模块间数据流
- [ ] 必须验证接口契约
- [ ] 测试数据必须独立
E2E 测试约束
- [ ] P0 场景必须 100% 覆盖
- [ ] 使用显式等待,禁止硬编码延时
详细指南见 templates/e2e-testing.md
需求追溯约束
- [ ] 每个需求必须有对应测试
测试骨架生成
AI 驱动的自顶向下开发:先生成测试骨架,后填充实现。
测试自说明规范
⛔ 测试代码不写 keel 编号标注。 追溯由 dev-workflow 的 Commit 2 写入文档侧的追溯矩阵(<repository>@<sha>),代码保持干净——一个没有 keel 上下文的维护者读到 @verifies AC-001 只会困惑。
测试要自说明的是行为,不是编号——测试名就是它的文档:
// ✅ 名字说清了「什么输入 → 什么结果」
test('createUser 传入无效邮箱格式时抛出 ValidationError', () => { ... });
// ❌ 名字什么都没说,失败时不提供任何诊断信息
test('works', () => { ... });
⛔ 不写 @verifies AC-XXX / @testcase UT-XXX 这类编号标注——AC 与测试编号的映射在 03-test-cases.md 的追溯矩阵里,代码里不留第二份。
骨架生成流程
03-test-cases.md (测试用例设计)
│
▼
生成测试骨架
├── describe 结构(按功能点分组)
├── test.skip() 占位(每个测试用例)
└── // TODO: 实现测试 注释
│
▼
逐个实现测试
├── 移除 skip
├── 编写 AAA 结构
└── 添加具体断言
骨架示例
// tests/user.service.test.ts
describe('UserService', () => {
describe('createUser', () => {
test.skip('应该拒绝无效邮箱格式', () => {
// TODO: 实现测试
// Arrange: 准备无效邮箱
// Act: 调用 createUser
// Assert: 验证抛出 ValidationError
});
test.skip('应该拒绝弱密码', () => {
// TODO: 实现测试
});
test.skip('应该拒绝重复用户名', () => {
// TODO: 实现测试
});
});
});
骨架生成约束
- [ ] 必须使用
test.skip()或test.todo()标记未实现测试 - [ ] 测试名必须说明验证的行为(不靠编号标注,靠名字自说明)
- [ ] 必须按功能点 (F-XXX) 组织 describe 结构
与 keel 协作
| 阶段 | Skill | 输入 | 输出 |
|------|-------|------|------|
| 测试设计 | /test-cases | 需求文档 | 测试用例矩阵 |
| 骨架生成 | /dev-tasks | 测试用例 | 测试骨架代码 |
| 测试实现 | /testing-guide | 骨架代码 | 完整测试 |
| 追溯同步 | /sync | 代码标注 | 更新矩阵 |
代码分支覆盖分析(AI 驱动)
代码实现完成后,AI 分析所有代码分支,找出未被测试覆盖的路径,生成补充测试用例。 这是对需求驱动测试(AC → 测试)的补充,不是替代。
定位
需求驱动测试(AC → 测试) ← 主路径,覆盖业务规则
+
代码分支分析(代码 → 测试) ← 补充路径,覆盖防御性逻辑
=
完整测试覆盖
适用场景
| 场景 | 说明 | |------|------| | 代码包含防御性逻辑 | 参数校验、null 检查、类型保护 | | 复杂条件分支 | switch/case、多条件组合 | | 覆盖率工具显示分支不足 | 行覆盖≥80% 但分支覆盖<80% | | 变异测试发现存活变异体 | 补充测试以杀死变异体 |
分析流程
Step 1: 分析代码分支
├── 条件语句(if/else, switch, 三元, &&/||)
├── 异常处理路径(try/catch, throw)
├── 早返回(guard clause)
└── 循环边界(空集合、单元素、多元素)
│
▼
Step 2: 映射现有测试
├── 匹配文档追溯矩阵的 AC 覆盖范围
├── 分析每个测试实际触发的分支
└── 标记已覆盖/未覆盖分支
│
▼
Step 3: 生成补充测试
├── 为未覆盖分支生成测试用例
├── 使用 BCA-XXX 编号
├── 添加 @covers-branch 标注
└── 遵循 AAA 结构和断言质量约束
标注规范(@covers-branch)
/**
* @covers-branch createUser:null-email-guard
*/
test('createUser 应该抛出错误当 email 为 null', () => {
// Arrange
const dto = { email: null, password: 'Strong1234' };
// Act & Assert
expect(() => createUser(dto)).toThrow('Email is required');
});
| 标注 | 用途 | 必须性 |
|------|------|--------|
| @covers-branch <函数>:<分支描述> | 标记覆盖的代码分支 | 必须 |
分支覆盖分析约束
- [ ] 分支分析在需求驱动测试之后执行(先 AC 测试,后分支补充)
- [ ] 补充测试必须标注 @covers-branch(区分需求驱动和分支补充)
- [ ] 补充测试使用 BCA-XXX 编号(不占用 UT/IT/E2E 编号空间)
- [ ] 业务逻辑分支应回溯为 AC 对应的正式测试(UT/IT/E2E),不保留为 BCA 编号
详细分析流程和示例见 templates/branch-coverage-analysis.md
Quick Reference
测试命名
[被测方法] 应该 [预期行为] 当 [条件]
测试结构 (AAA)
Arrange → Act → Assert (具体断言)
禁止的弱断言
// ❌ 禁止
expect(result).toBeDefined();
expect(result).toBeTruthy();
expect(result).not.toBeNull();
// ✅ 要求
expect(result.status).toBe('success');
expect(result.items).toHaveLength(3);
常用命令
# 覆盖率
npm test -- --coverage # Jest
pytest --cov=src # pytest
go test -cover ./... # Go
# 变异测试
npx stryker run # JS/TS
mutmut run # Python
mvn pitest:mutationCoverage # Java
模板索引
核心指南
| 模板 | 说明 | |------|------| | core-concepts.md | 测试核心理念与质量金字塔详解 | | unit-testing.md | 单元测试完整指南 | | integration-testing.md | 集成测试策略与示例 | | e2e-testing.md | E2E 测试最佳实践 |
工具配置
| 模板 | 说明 | |------|------| | branch-coverage-analysis.md | AI 驱动的代码分支覆盖分析详解 | | mutation-testing.md | 8种语言变异测试配置 | | ci-integration.md | CI/CD 集成配置 | | traceability-matrix.md | 需求追溯矩阵 | | test-examples.md | 测试代码示例集 |
语言最佳实践
| 语言 | 框架 | 模板 | |------|------|------| | JavaScript/TypeScript | Jest, Vitest | best-practices/jest-vitest.md | | Python | pytest | best-practices/pytest.md | | Java | JUnit 5 | best-practices/junit5.md | | C# / .NET | xUnit, NUnit | best-practices/xunit.md | | Go | testing, testify | best-practices/go.md | | Rust | cargo test | best-practices/rust.md | | Swift | XCTest | best-practices/swift.md | | C/C++ | Google Test | best-practices/googletest.md |
与其他 Skills 协作
| 场景 | Skill |
|------|-------|
| 测试用例设计 | /test-cases |
| 代码可测试性 | /code-quality |
| 重构前测试 | /refactor |
| 分支覆盖分析 | /dev-workflow — 完成检查阶段可选调用 |
微信扫一扫