tdd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Test-Driven Development

测试驱动开发

TDD is a sequence of small red → green → refactor cycles. Make the intended behavior executable and observe its failure before implementing it. A completed cycle leaves the affected path in a production-quality shape; a blocked cycle preserves the valid failing test and reports what prevents completion.
When exploring the codebase, read
CONTEXT.md
(if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
TDD 由一系列小型的「红 → 绿 → 重构」循环构成。在实现功能前,先将预期行为转化为可执行的测试,并观察测试失败的结果。一个完整的循环会让受影响的代码路径达到生产级质量;如果循环受阻,需保留有效的失败测试,并说明阻碍完成的原因。
探索代码库时,请阅读
CONTEXT.md
(如果存在),确保测试名称和接口术语与项目的领域语言一致,并遵守你所修改区域的 ADR。

What a good test is

什么是好的测试

Tests protect a meaningful contract through an interface or observation that can detect a realistic regression. Prefer tests that survive implementation-only refactors of that contract. A test such as "user can checkout with valid cart" explains the capability it protects; changing the contract itself may legitimately require changing its tests.
See tests.md for examples and mocking.md for mocking guidelines.
测试通过能够检测真实回归的接口或观测方式,守护有意义的契约。优先选择在仅重构实现(不改变契约)时依然有效的测试。像「用户可使用有效购物车结账」这样的测试,清晰说明了它所保护的能力;如果契约本身发生变化,相应调整测试是合理的。
示例请参见 tests.md,Mock 相关规范请参见 mocking.md。

Seams: where tests go

接缝:测试的放置位置

A seam is a boundary where a test exercises behavior or controls a dependency. It may be a public API or an internal interface owned by a module. An internal module can have its own contract and meaningful tests without exposing that interface to application callers. Do not bypass encapsulation merely to assert on incidental private state.
Before writing a test, identify the requirement that establishes the expected outcome, the starting conditions, and the realistic failure it should catch. Then choose where to call the code or control its dependencies. Prefer existing interfaces and nearby test conventions. Use an internal interface when its tests catch important failures that other tests would miss. Do not expose private state or add an abstraction just to make a test easier to write. Ask only when the choice would change the agreed scope or a contract.
Testing effort should land on critical paths and complex logic rather than every edge case.
When the shape of the interface is itself in question—how deep the module is, where the seam belongs, or what the interface should expose—use the
codebase-design
skill for the shared module and seam vocabulary.
接缝是测试验证行为或控制依赖的边界。它可以是公共 API,也可以是模块内部维护的接口。内部模块可以有自己的契约和有意义的测试,无需将该接口暴露给应用调用方。不要仅仅为了断言无关的私有状态而破坏封装。
编写测试前,先明确支撑预期结果的需求、起始条件,以及测试应当捕获的真实故障。然后选择调用代码或控制其依赖的位置。优先使用现有接口和周边已有的测试约定。当内部接口的测试能够捕获其他测试会遗漏的重要故障时,可以使用内部接口。不要仅仅为了简化测试编写,就暴露私有状态或引入抽象层。只有当选择会影响约定的范围或契约时,才需要征询相关方意见。
测试工作应当聚焦于关键路径和复杂逻辑,而非覆盖所有边界情况。
当接口的形态本身存在疑问时——比如模块的深度、接缝的位置,或者接口应当暴露什么——请使用
codebase-design
Skill 来统一共享模块和接缝的术语。

Anti-patterns

反模式

  • Implementation-coupled: asserts incidental private state, helper calls, or ordering that the tested contract does not promise. Internal tests, collaborator doubles, and direct database observations are not inherently coupled; judge whether they detect a real contract failure or merely freeze the implementation's shape.
  • Tautological or non-independent expectations: an assertion that derives its expected value from the result under test cannot provide independent evidence. Copying production logic into the expected-value calculation can also reproduce the same bug. Use expectations grounded independently in the contract, a worked example, or a trusted oracle; a calculated expectation is not automatically tautological.
  • Writing all tests before any implementation: this can commit the tests to a design before trying it in working code. Write one test, implement its behavior, and use what you learn to choose the next test.
  • 实现耦合:断言无关的私有状态、辅助函数调用或执行顺序,而这些并非被测契约所承诺的内容。内部测试、协作对象替身、直接数据库观测本身并不一定是耦合的;判断标准是它们是在检测真实的契约故障,还是仅仅固化了实现的形态。
  • 同义反复或非独立预期:从被测结果中推导预期值的断言无法提供独立证据。将生产逻辑复制到预期值计算中也可能复现相同的缺陷。预期值应当独立基于契约、实际案例或可信基准;计算得出的预期并不自动属于同义反复。
  • 在实现前编写所有测试:这会导致在实际代码中验证设计之前,就将测试绑定到某个设计上。正确的做法是先写一个测试,实现对应的行为,再用过程中积累的经验选择下一个测试。

Feature cycle

功能开发循环

Work in vertical slices:
  1. Choose the smallest observable behavior at an established or agreed seam.
  2. Write one focused test that specifies that behavior.
  3. Run it before changing production code. Confirm that it fails because the behavior is missing, not because the test is broken.
  4. Make the simplest production-quality change that passes the test without adding future behavior. Extract a focused module when it makes responsibilities clearer, even if it has only one caller.
  5. Rerun the test and confirm that it passes.
  6. Refactor the affected path when needed to preserve clear ownership and cohesion, then confirm the test still passes.
  7. Repeat with the next behavior, allowing each cycle to inform the next.
以垂直切片的方式开展工作:
  1. 在已建立或约定好的接缝处,选择最小的可观测行为。
  2. 编写一个聚焦于该行为的测试。
  3. 在修改生产代码之前运行测试,确认测试失败是因为缺少对应行为,而非测试本身存在问题。
  4. 做出最简单的、生产级质量的修改来通过测试,不要提前添加未来才需要的功能。当提取聚焦模块能让职责更清晰时,就进行提取,哪怕该模块只有一个调用方。
  5. 重新运行测试,确认其通过。
  6. 必要时重构受影响的代码路径,以保持清晰的职责归属和内聚性,随后确认测试依然通过。
  7. 对下一个行为重复上述流程,让每个循环为下一个循环提供参考。

Bug-fix cycle

缺陷修复循环

When a bug has a clear, practical regression path:
  1. Identify the intended behavior, current behavior, affected path, and smallest observable reproduction.
  2. Choose the narrowest executable check already used near that codepath.
  3. Add the smallest focused regression test that would have caught the bug.
  4. Run it before fixing the implementation. Confirm that it fails for the intended reason; correct the test or reproduction if it passes or fails for an unrelated reason.
  5. Make a focused production-quality change that restores the intended behavior while preserving nearby contracts and clear ownership.
  6. Rerun the regression test, then run relevant adjacent tests, type checks, lint, or scenario checks in proportion to the change's risk.
当缺陷有清晰、可复现的回归路径时:
  1. 明确预期行为、当前行为、受影响的代码路径,以及最小的可观测复现步骤。
  2. 选择该代码路径附近已在使用的、最小范围的可执行检查方式。
  3. 添加最小的、聚焦的回归测试,该测试应当能够提前捕获这个缺陷。
  4. 在修复实现之前运行测试,确认测试失败的原因符合预期;如果测试通过或因无关原因失败,修正测试或复现步骤。
  5. 做出聚焦的、生产级质量的修改,恢复预期行为,同时保留周边契约和清晰的职责归属。
  6. 重新运行回归测试,然后根据修改的风险程度,运行相关的相邻测试、类型检查、Lint 或场景检查。

When a failing test is impractical

当无法编写失败测试时

Do not create substantial test infrastructure, brittle mocks, slow end-to-end setup, production-only state, or broad fixture churn merely to satisfy the workflow. Prefer no new test over a test with weak or misleading signal.
Before implementing the behavior, explain the concrete limitation and choose a check that can demonstrate the missing behavior and its correction. This may be a script, manual command, browser workflow, log assertion, or integration check. It must exercise the required behavior, not merely nearby code that already works. If execution is unavailable, report the evidence gap; do not substitute a test that bypasses it.
A test added after implementation can protect against a regression, but is not evidence of test-first work. If it already passes, inspect whether the behavior exists or the check misses it. Do not manufacture a failure just to claim a red phase. Running a later regression against the old implementation can establish sensitivity; report that sequence accurately.
不要仅仅为了满足工作流要求,就搭建大量测试基础设施、脆弱的 Mock、缓慢的端到端环境、仅生产环境存在的状态,或者大规模变动测试夹具。宁可没有新测试,也不要编写信号弱或具有误导性的测试。
在实现行为之前,说明具体的限制条件,并选择一种能够演示缺失行为及其修复方式的检查手段。这可以是脚本、手动命令、浏览器操作流程、日志断言或集成检查。它必须验证所需的行为,而不仅仅是周边已经正常运行的代码。如果无法执行测试,需说明证据缺口;不要用绕过该行为的测试来替代。
实现之后添加的测试可以防止回归,但不能作为测试优先工作的证据。如果测试已经通过,需检查是行为已经实现,还是检查没有捕获到问题。不要为了声称完成了红阶段而人为制造失败。针对旧实现运行后续的回归测试可以验证测试的敏感度;请准确报告该过程。

Preserve the expectation

保留预期

Change a test only when the authorized requirement changed, evidence shows the test was wrong against that requirement, or an interface/setup change preserves the same protected behavior. State the reason for a material expectation change. The implementation's new output is not authority to change its expected output.
This applies to assertions, fixtures, mocks, snapshots, skips, expected-failure markers, and test selection. Do not make a failing case pass by removing its relevant starting conditions, accepting its error as success, or bypassing the failing collaborator. When replacing a test, retain its meaningful failure cases unless their contract was explicitly retired.
Distinguish a test defect, unavailable execution, and a real contract failure. Fix a broken reproduction without changing the intended outcome. A setup or infrastructure error is not the required behavioral red; preserve and report it separately if it prevents reaching that behavior.
仅当授权需求发生变更、有证据表明测试不符合该需求,或者接口/测试搭建方式的变更未改变所保护的行为时,才可以修改测试。重大预期变更需说明原因。实现的新输出不能作为修改预期输出的依据。
这适用于断言、测试夹具、Mock、快照、跳过标记、预期失败标记和测试选择。不要通过移除相关起始条件、将错误视为成功,或者绕过失败的协作对象,来让失败的用例通过。替换测试时,除非其契约已明确废弃,否则需保留其有意义的失败用例。
要区分测试缺陷、无法执行和真实的契约故障。在不改变预期结果的前提下,修复有问题的复现步骤。测试搭建或基础设施错误不是所需的行为红阶段;如果它阻碍了验证目标行为,需单独记录并报告。

When green is blocked

当绿阶段受阻时

Continue authorized implementation and necessary restructuring while they can satisfy the expectation. Refactoring after green does not prohibit structural changes needed to reach green.
If a valid failure requires an unresolved domain decision, unavailable prerequisite, or material scope expansion, preserve the failing test and completed work. Report the expected and actual behavior, the command and failure observed, and the smallest prerequisite or decision needed. Continue independent in-scope work where useful. Do not weaken the test or disable the feature to make validation pass.
A pre-existing defect can still block the requested behavior; its age does not make acceptance optional. A blocked implementation is unfinished. A request only to reproduce a bug or add a failing regression can be complete with that test still red; it does not authorize fixing production code.
在能够满足预期的前提下,继续进行授权的实现工作和必要的结构调整。绿阶段后的重构并不禁止为了达到绿阶段所需的结构变更。
如果有效的测试失败需要未解决的领域决策、不可用的前置条件,或重大的范围扩展,请保留失败的测试和已完成的工作。报告预期行为和实际行为、执行的命令和观察到的失败结果,以及所需的最小前置条件或决策。在可行的情况下,继续开展独立的范围内工作。不要为了让验证通过而削弱测试或禁用功能。
预先存在的缺陷仍然可能阻碍请求的行为;它的存在时间长短不代表可以接受。受阻的实现是未完成的。如果请求仅仅是复现缺陷或添加失败的回归测试,那么即使测试仍然是红的,任务也可以完成;这并不授权修改生产代码。

Validation cadence

验证节奏

Within each red → green → refactor cycle, run the focused check and any adjacent checks affected by the current change. Run broader validation when a shared contract, integration risk, failure, or repository rule makes it necessary, not automatically after every slice.
At completion, satisfy applicable repository checks and meaningful regression coverage. Reuse results that still cover the final implementation and relevant conditions; rerun only invalidated or failed checks unless a repository rule requires otherwise. Do not repeat a full suite, build, or lint pass merely to mark another cycle complete.
Verify that the relevant tests actually ran under the reported command. Report passed, failed, skipped, and unable-to-run checks distinctly. Required coverage hidden behind an opt-in or missing prerequisite remains a verification gap, even when the command exits successfully.
在每个「红 → 绿 → 重构」循环中,运行聚焦的检查,以及当前修改影响到的所有相邻检查。只有当共享契约、集成风险、故障或仓库规则有必要时,才运行更广泛的验证,而不是在每个切片之后都自动运行。
完成时,需满足适用的仓库检查要求和有意义的回归覆盖。复用仍然覆盖最终实现和相关条件的结果;仅重新运行失效或失败的检查,除非仓库规则另有要求。不要仅仅为了标记另一个循环完成,就重复运行完整的测试套件、构建或 Lint 检查。
需验证相关测试确实在报告的命令下运行了。明确区分报告通过、失败、跳过和无法运行的检查。隐藏在可选开启或缺少前置条件之后的所需覆盖,仍然是验证缺口,即使命令成功退出。

Guardrails

护栏准则

  • Red before green. Write the failing test first, then implement the current behavior completely. Don't anticipate future tests or add speculative features.
  • One behavior at a time. Keep each cycle focused on one coherent contract change. Use the observations and dependency controls needed to prove it without batching unrelated behavior into the same cycle.
  • Keep a regression test focused on the reported bug; avoid unrelated coverage expansion or fixture churn.
  • If a bug is flaky, make the regression signal deterministic where practical and state what signal is being locked down.
  • If a bug exposes a broader class of failures, establish the focused regression path first, then consider sibling coverage.
  • Refactor after green when the changed path needs it. Preserve clear ownership and cohesion before starting the next slice. Keep the refactor within the affected behavior; do not turn the cycle into unrelated cleanup.
  • 先红后绿。 先编写失败的测试,再完整实现当前行为。不要预判未来的测试或添加投机性功能。
  • 一次一个行为。 每个循环聚焦于一个连贯的契约变更。使用所需的观测和依赖控制来验证它,不要把不相关的行为放到同一个循环里。
  • 回归测试要聚焦于报告的缺陷;避免不相关的覆盖范围扩展或测试夹具变动。
  • 如果缺陷是偶现的,在可行的情况下让回归信号具有确定性,并说明正在锁定的信号是什么。
  • 如果缺陷暴露了更广泛的故障类别,先建立聚焦的回归路径,再考虑同类覆盖。
  • 绿阶段后按需重构。 在开始下一个切片之前,保持清晰的职责归属和内聚性。将重构限制在受影响的行为范围内;不要把循环变成不相关的清理工作。

Final response

最终回复

Report the evidence, not only the outcome:
  • Name the failing-before test or executable check and the failure it produced.
  • Name the passing-after test run, or the still-failing regression and blocker, with any nearby validation performed.
  • If failing-before evidence could not be demonstrated, state why and describe the closest regression check used instead.
报告证据,而不仅仅是结果:
  • 说明实现前失败的测试或可执行检查的名称,以及它产生的失败结果。
  • 说明实现后通过的测试运行结果,或者仍然失败的回归测试和阻碍因素,以及执行的所有周边验证。
  • 如果无法演示实现前失败的证据,说明原因,并描述所使用的最接近的回归检查。