name: tdd description: | 使用红绿重构周期的测试驱动开发促进。引导用户先写测试,实现最小化代码通过,并重构以提高质量。当用户想要练习TDD、需要先写测试、以测试为先开发新功能或需要测试结构和实现指导时使用。触发词包括’使用TDD’、‘测试驱动开发’、‘先写测试’、'红绿重构’或请求用测试开发功能。 allowed-tools: “*”
测试驱动开发 (TDD) 技能
使用红绿重构周期引导用户进行严格的测试优先开发。
核心 TDD 工作流程
红 → 绿 → 重构 → 重复
红:写一个失败的测试
- 识别下一个要实现的小行为
- 写测试指定该行为
- 运行测试以验证其因正确原因失败
- 如果测试意外通过,测试是错误的
绿:使其通过
- 写最小代码以使测试通过
- 不要担心完美性
- 最简单的解决方案
- 运行测试以验证其通过
重构:改进代码
- 改进代码质量同时保持测试通过
- 移除重复
- 改进名称和结构
- 每次更改后运行测试以确保仍然通过
重复
- 测试通过时提交
- 识别下一个行为
- 从新测试开始下一个周期
TDD 纪律
要遵循的关键规则:
测试优先(红阶段)
- 总是在实现前写测试
- 抵制先写代码的冲动
- 测试定义了什么意味着“完成”
- 在使其通过前看到测试失败
最小实现(绿阶段)
- 写最简单的代码以通过
- 不要过度工程化
- 不要添加未测试的功能
- 一次一个测试
仅在绿时重构
- 不要在有失败测试时重构
- 在重构期间保持测试通过
- 小的、增量式改进
- 每次重构步骤后运行测试
频繁运行测试
- 写测试后(应失败)
- 写实现后(应通过)
- 每次重构步骤后(应保持通过)
- 提交前
何时使用此技能
为涉及以下请求激活:
- “使用TDD…” / “测试驱动开发…”
- “先写测试…” / “红绿重构…”
- 以测试为先开发新功能
- 学习TDD实践
- 设置测试基础设施
- 测试设计和组织
测试结构模式
安排-执行-断言 (AAA)
安排 - 设置测试数据和环境 执行 - 执行被测试的代码 断言 - 验证结果
def test_add_two_numbers():
calculator = Calculator() # 安排
result = calculator.add(2, 3) # 执行
assert result == 5 # 断言
给定-当-则 (BDD 风格)
给定 - 初始上下文/前提条件 当 - 发生动作/事件 则 - 预期结果
测试设计原则
测试什么
- 公共接口 - 测试用户依赖的行为
- 边缘情况 - 边界、空输入、最大值
- 错误条件 - 无效输入、异常
- 业务逻辑 - 核心算法和规则
- 集成点 - 组件交互处
不测试什么
- 私有实现细节 - 测试行为而非内部
- 第三方库 - 信任它们工作,测试你的使用
- 简单getter/setter - 除非它们有逻辑
- 框架代码 - 测试你的代码,而非框架
每个测试一个行为
- 每个测试应验证单一行为
- 使故障更易诊断
- 保持测试专注和可读
- 首选多个小测试而非一个大测试
使测试可读
- 描述性名称 -
test_add_returns_sum_of_two_positive_numbers - 清晰结构 - AAA 或给定-当-则
- 自文档化 - 测试显示代码应如何使用
- 最小设置 - 仅此测试所需
保持测试独立
- 测试不应相互依赖
- 测试可以任意顺序运行
- 每个测试从清洁状态开始
- 测试间无共享可变状态
参见 references/test-design-patterns.md 获取全面指导。
语言特定指导
对于 Python
参见 references/python-tdd.md 获取:
- pytest 和 unittest 框架
- 装置和参数化测试
- 使用 unittest.mock 模拟
- 测试异步代码
- 使用 pytest-cov 覆盖
- 运行和组织测试
对于 Emacs Lisp
参见 references/elisp-tdd.md 获取:
- ERT (Emacs Lisp 回归测试)
- 测试交互函数
- 缓冲区操作测试
- 使用 cl-letf 模拟
- Buttercup (BDD 替代)
- 在 Emacs 和批处理模式运行测试
对于其他语言
参见 references/general-tdd.md 获取:
- 查找测试框架
- 通用测试模式
- 常见测试概念
- 构建工具集成
- 语言无关原则
测试类型及何时使用
单元测试
什么: 隔离测试单个函数/方法
何时:
- 测试纯函数
- 测试业务逻辑
- 测试算法
- 快速、专注的测试
示例: test_calculate_discount(price, percentage)
集成测试
什么: 测试多个组件协同工作
何时:
- 测试数据库交互
- 测试 API 调用
- 测试服务集成
- 验证组件正确连接
示例: test_user_service_saves_to_database()
端到端测试
什么: 测试完整用户工作流
何时:
- 测试关键用户路径
- 验证系统整体
- 部署冒烟测试
示例: test_user_can_register_and_login()
测试金字塔:
/\ ← 少端到端测试(慢、脆弱)
/ \
/ IT \ ← 一些集成测试
/______\
/ 单元 \ ← 多单元测试(快速、专注)
/__________\
TDD 红绿重构示例
目标: 实现阶乘函数
迭代 1 - 基本案例:
- 红:
test_factorial_of_zero_is_one()→ ❌ 阶乘未定义 - 绿:
def factorial(n): return 1→ ✅ 通过 - 重构:暂无。提交。
迭代 2 - 正数:
- 红:
test_factorial_of_five()期望 120 → ❌ 得到 1 - 绿:实现循环计算阶乘 → ✅ 通过
- 重构:使用递归以提高优雅性 → ✅ 仍然通过。提交。
迭代 3 - 错误处理:
- 红:
test_factorial_negative_raises_error()→ ❌ 未引发错误 - 绿:添加 if n < 0: raise ValueError → ✅ 所有测试通过
- 重构:添加文档字符串 → ✅ 仍然通过。提交。
完成! 函数完整、全测试、文档化。三个测试驱动迭代。
模拟和测试替身
何时模拟
- 外部依赖 - 数据库、API、文件系统
- 慢操作 - 网络调用、大计算
- 不可预测行为 - 随机、时间依赖、外部状态
- 难触发场景 - 错误条件、边缘案例
何时不模拟
- 你自己的代码 - 首选真实对象用于你的代码
- 简单对象 - 数据类、值对象
- 被测试逻辑 - 不要模拟你正在测试的内容
测试替身类型
模拟 - 用期望编程,验证交互 存根 - 提供固定响应,不验证 伪造 - 工作实现,比真实简单 间谍 - 记录调用,允许后验证
参见 references/test-design-patterns.md 获取详细模拟策略。
常见 TDD 反模式
不要:
❌ 在测试前写实现 - 违背 TDD 目的
❌ 在使测试通过前写多个测试 - 保持节奏(一个测试,使其通过,下一个测试)
❌ 在红测试时重构 - 仅在绿时重构
❌ 测试实现细节 - 测试行为,而非内部
❌ 跳过重构步骤 - 技术债务积累
❌ 写难理解的测试 - 测试是文档
❌ 在测试间创建依赖 - 测试必须独立
❌ 模拟所有 - 实际时使用真实对象
❌ 永远用硬编码值假装 - “假装直到你实现”是临时的
❌ 写慢测试 - 慢测试套件不会频繁运行
测试命名约定
好测试名称是描述性和具体的:
模式:test_<函数>_<场景>_<预期结果>
test_add_two_positive_numbers_returns_sum()
test_add_with_negative_number_returns_correct_result()
test_add_with_zero_returns_other_number()
模式:should_<预期行为>_when_<条件>
should_return_empty_list_when_no_items_match()
should_raise_error_when_input_is_null()
should_calculate_discount_when_user_is_premium()
模式:<行为>_<状态>_<预期>
(ert-deftest save-buffer-modified-saves-to-file ())
(ert-deftest load-file-missing-raises-error ())
测试名称应:
- 描述被测试内容
- 描述场景/条件
- 描述预期结果
- 作为文档可读
测试组织
目录结构
Python:
项目/
├── src/
│ └── calculator.py
└── tests/
├── __init__.py
├── test_calculator.py
└── conftest.py # pytest 装置
Elisp:
包/
├── my-package.el
└── test/
└── test-my-package.el
命名约定
- 测试文件:
test_*.py,*_test.py,test-*.el - 测试函数:以
test_或ert-deftest开头 - 测试类:
Test*(如果使用类)
分组测试
- 一般每个源文件一个测试文件
- 在同一文件中分组相关测试
- 分离单元/集成/端到端测试
参见语言特定参考获取详细组织模式。
使用测试重构
安全重构过程
- 确保所有测试为绿 前开始
- 做小更改 - 一次重构
- 每次更改后运行测试 - 立即捕获中断
- 频繁提交 - 当测试通过时
- 重构时不添加功能 - 分离关注点
常见重构
- 提取函数(分解大函数)
- 重命名以提高清晰度
- 移除重复(DRY)
- 简化条件逻辑
- 提取变量以提高可读性
- 内联不必要抽象
重构时测试中断
如果测试实现细节:
- 更新测试以测试行为
- 使测试对更改更有弹性
如果测试行为:
- 修复代码,而非测试
- 重构期间行为不应改变
如果太多测试中断:
- 更改太大,回滚
- 做更小的增量更改
参见 references/refactoring-with-tests.md 获取详细指导。
测试覆盖
覆盖测量哪些代码由测试执行,而非测试是否好。
覆盖类型
- 行覆盖 - 哪些行执行
- 分支覆盖 - 哪些路径采取
- 函数覆盖 - 哪些函数调用
覆盖目标
- 目标高覆盖 (80%+) 但非 100%
- 100% 覆盖不意味无错误
- 关注关键代码路径
- 不要仅为增加覆盖而测试
使用覆盖工具
- Python: pytest-cov, coverage.py
- JavaScript: Jest with coverage
- Java: JaCoCo
- Ruby: SimpleCov
使用 scripts/coverage_analyzer.py 识别覆盖缺口。
使用支持资源
本技能附加资源:
- references/python-tdd.md: 全面 Python 测试指导(pytest, unittest, 模拟, 异步)
- references/elisp-tdd.md: 全面 Elisp 测试指导(ERT, Buttercup, 交互函数)
- references/general-tdd.md: 任何语言的通用 TDD 原则
- references/test-design-patterns.md: 测试什么、测试组织、反模式
- references/refactoring-with-tests.md: 安全重构过程和常见重构
- scripts/test_template_generator.py: 生成测试文件模板
- scripts/coverage_analyzer.py: 分析覆盖报告
- assets/templates/: 多语言测试文件模板
快速参考
TDD 周期:
- 红 - 写失败测试
- 绿 - 使其通过(最小代码)
- 重构 - 改进同时保持绿
- 重复
测试结构:
- 安排(设置)
- 执行(执行)
- 断言(验证)
测试原则:
- 测试优先
- 一次一个测试
- 每个测试一个行为
- 独立测试
- 快速测试
- 可读测试
重构规则:
- 仅在绿时重构
- 小更改
- 频繁运行测试
- 不添加功能
记住: TDD 是纪律。价值来自严格遵循周期。先测试。看到失败。使其通过。重构。重复。节奏创造质量代码。