test-driven-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTest-Driven Development
测试驱动开发(TDD)
Write tests before code. The test is the specification. If you can't write a test, you don't understand the requirement.
Related Skills:
- Query for Kavak-specific testing patterns, kbroker event testing, STS mockingkavak-documentation- Use
MCP tool for testing best practices at Kavakkavak-platform/plati_query
先编写测试,再编写代码。测试就是需求规格说明书。如果你无法编写测试,说明你还没理解需求。
相关技能:
- 查询Kavak专属测试模式、kbroker事件测试、STS模拟kavak-documentation- 使用
MCP工具获取Kavak的测试最佳实践kavak-platform/plati_query
Quick Start
快速开始
bash
undefinedbash
undefined1. Write failing test first
1. 先编写失败的测试
2. Run to see it fail (RED)
2. 运行测试确认失败(红阶段)
3. Write minimal code to pass (GREEN)
3. 编写最少代码让测试通过(绿阶段)
4. Refactor while tests pass (REFACTOR)
4. 在测试通过的前提下重构代码(重构阶段)
5. Repeat
5. 重复上述步骤
**Test commands by language:**
| Language | Run Tests | Watch Mode |
|----------|-----------|------------|
| Go | `go test ./...` | `go test ./... -v` |
| Node/TS | `npm test` | `npm test -- --watch` |
| Python | `pytest` | `pytest-watch` |
| Java | `./mvnw test` | `./mvnw test -Dtest=*` |
**各语言测试命令:**
| 语言 | 运行测试 | 监听模式 |
|----------|-----------|------------|
| Go | `go test ./...` | `go test ./... -v` |
| Node/TS | `npm test` | `npm test -- --watch` |
| Python | `pytest` | `pytest-watch` |
| Java | `./mvnw test` | `./mvnw test -Dtest=*` |The Red-Green-Refactor Cycle
红-绿-重构周期
1. RED: Write Failing Test
1. 红阶段:编写失败的测试
- Write ONE test for the next piece of behavior
- Test must fail for the RIGHT reason
- Use descriptive names: should_calculate_total_with_tax
- Follow Arrange-Act-Assert structure- 为下一个待实现的行为编写**一个**测试
- 测试必须因**正确的原因**失败
- 使用描述性命名:如should_calculate_total_with_tax
- 遵循“准备-执行-断言”(Arrange-Act-Assert)结构2. GREEN: Make It Pass
2. 绿阶段:让测试通过
- Write MINIMAL code to pass the test
- Don't optimize, don't refactor, don't add features
- "Fake it till you make it" is valid
- The goal is GREEN, not perfect- 编写**最少**的代码让测试通过
- 不要优化、不要重构、不要额外添加功能
- “先伪造实现再完善”是可行的策略
- 当前目标是让测试变绿,而非追求完美代码3. REFACTOR: Improve Design
3. 重构阶段:优化设计
- Clean up code while tests stay green
- Remove duplication
- Improve names
- Extract methods/functions
- Run tests after EVERY change- 在测试保持通过的前提下清理代码
- 消除重复代码
- 优化命名
- 提取方法/函数
- 每做一次修改都要重新运行测试Common Rationalizations (Resist Them)
常见自我辩解(请抵制)
| Rationalization | Counter |
|---|---|
| "Let me just write one more method" | Stop. Test what exists first |
| "I'll add tests after" | You won't. Tests written after verify nothing |
| "It's too simple to test" | Simple now, complex later. Test it |
| "I'll refactor tests later" | Refactor production code, not test structure |
| "This is just scaffolding" | Scaffolding becomes foundation. Test it |
| 自我辩解 | 反驳理由 |
|---|---|
| “让我先再多写一个方法” | 停下来,先测试已有的代码 |
| “我之后再补测试” | 你大概率不会补。事后写的测试无法验证任何内容 |
| “这个功能太简单,没必要测试” | 现在简单,后续可能变复杂。一定要测试 |
| “我之后再重构测试” | 重构生产代码即可,不要改动测试结构 |
| “这只是脚手架代码” | 脚手架会变成系统基础,必须测试 |
Anti-Patterns (What NOT to Do)
测试反模式(切勿这样做)
| Anti-Pattern | Problem | Fix |
|---|---|---|
| The Liar | Test passes but tests nothing | Assert actual behavior |
| The Mockery | Over-mocking hides real bugs | Mock boundaries only |
| Excessive Setup | 50 lines setup, 2 lines test | Simplify SUT or use builders |
| The Slow Poke | Tests take minutes | Isolate, mock I/O |
| The Local Hero | Passes locally, fails in CI | No env dependencies |
| Test-per-Method | 1:1 test-to-method | Test behaviors, not methods |
| 反模式 | 问题 | 解决方法 |
|---|---|---|
| 虚假测试 | 测试通过但未验证任何内容 | 断言实际行为 |
| 过度模拟 | 过多模拟会掩盖真实Bug | 仅对系统边界进行模拟 |
| 过度准备 | 50行准备代码,仅2行测试逻辑 | 简化被测系统(SUT)或使用构建器模式 |
| 慢速测试 | 测试需要数分钟才能完成 | 隔离依赖、模拟I/O操作 |
| 本地英雄 | 本地运行通过,CI环境运行失败 | 测试不要依赖特定环境 |
| 按方法测试 | 测试与方法1:1对应 | 测试行为,而非测试方法 |
Verification Checklist
验证检查清单
Before committing, verify your tests:
markdown
[ ] Test fails when behavior is removed?
[ ] Test name describes the behavior?
[ ] Arrange-Act-Assert structure clear?
[ ] No test-only code in production?
[ ] Mocks verify behavior, not implementation?
[ ] Edge cases covered?提交代码前,请验证以下测试要点:
markdown
[ ] 移除对应行为后,测试会失败吗?
[ ] 测试名称能准确描述行为吗?
[ ] “准备-执行-断言”结构清晰吗?
[ ] 生产代码中没有仅为测试编写的代码吗?
[ ] 模拟验证的是行为而非实现细节吗?
[ ] 边界情况已覆盖吗?When TDD Is Mandatory
必须使用TDD的场景
- New features (write test first)
- Bug fixes (write failing test that reproduces bug)
- Refactoring (tests protect behavior)
- API changes (contract tests first)
- 新功能开发(先写测试)
- Bug修复(先编写能复现Bug的失败测试)
- 代码重构(测试保障原有行为不被破坏)
- API变更(先编写契约测试)
When to Adapt TDD
可调整TDD的场景
- Exploratory/spike work (delete code after, then TDD)
- UI prototyping (test logic, not layout)
- Legacy code (add tests before changing)
- 探索性/ spike 开发(写完代码后删除,再用TDD重新实现)
- UI原型开发(测试业务逻辑,而非布局)
- 遗留代码改造(修改前先添加测试)
Test Naming Convention
测试命名规范
should_[expected_behavior]_when_[condition]
Examples:
- should_return_zero_when_cart_is_empty
- should_throw_error_when_user_not_found
- should_apply_discount_when_coupon_validshould_[预期行为]_when_[触发条件]
示例:
- should_return_zero_when_cart_is_empty
- should_throw_error_when_user_not_found
- should_apply_discount_when_coupon_validReferences
参考资料
| Reference | Purpose |
|---|---|
| Detailed cycle walkthrough |
| Full anti-pattern catalog |
| Go TDD examples |
| Node/TypeScript TDD examples |
| Python TDD examples |
| Java TDD examples |
| Pre-commit verification |
| What to mock, what not to mock |
| 参考资料 | 用途 |
|---|---|
| 红-绿-重构周期详细指南 |
| 完整的测试反模式目录 |
| Go语言TDD示例 |
| Node/TypeScript TDD示例 |
| Python TDD示例 |
| Java TDD示例 |
| 提交前验证清单 |
| 模拟边界指南 |
Best Practices
最佳实践
- One assertion per test - Multiple assertions hide failures
- Test behavior, not implementation - Tests survive refactoring
- Isolated tests - No shared state between tests
- Fast tests - Under 100ms per unit test
- Deterministic - Same result every run
- Self-documenting - Test name = specification
Principle: If you can't write a test for it, you don't understand what it should do. The test IS the specification.
- 每个测试一个断言 - 多个断言会隐藏失败原因
- 测试行为而非实现 - 测试能在重构后依然有效
- 测试隔离 - 测试之间无共享状态
- 快速测试 - 单元测试耗时应低于100毫秒
- 确定性 - 每次运行结果一致
- 自文档化 - 测试名称就是需求说明
核心原则:如果你无法为某个功能编写测试,说明你还没明确它应该做什么。测试就是需求规格说明书。