Install
openclaw skills install @carolz1/tddTest-driven development. Use when building a feature or fixing a bug test-first, when the user mentions "red-green-refactor", "TDD", or asks for tests before code. Enforces the iron law "no production code without a failing test first" with a red-green-refactor loop, anti-patterns, and cross-framework guidance.
openclaw skills install @carolz1/tddA red → green → refactor loop discipline that keeps tests worth keeping. / 一个能让测试经得起时间考验的红 → 绿 → 重构循环纪律。
Do NOT use when / 不适用:
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
没有失败的测试,就不能写生产代码。
If you wrote code first, delete it and start over. "I'll write the test after" is the rationalization that defeats TDD. / 如果你先写了代码,删掉重写。"之后补测试"是击败 TDD 的合理化借口。
┌──────────────────────────────────────────┐
│ RED Write a failing test │
│ 红 写一个会失败的测试 │
│ ↓ │
│ RUN Confirm it fails for the right │
│ 跑 reason (not a typo/setup error) │
│ 确认失败原因是「业务缺失」 │
│ 而不是「拼写/环境」错误 │
│ ↓ │
│ GREEN Write the minimum code to pass │
│ 绿 写最少代码让测试通过 │
│ ↓ │
│ RUN Confirm it passes │
│ 跑 确认通过 │
│ ↓ │
│ REFACTOR Clean up (test + code together)│
│ 重构 一起清理(测试和代码同时) │
│ ↓ │
│ COMMIT Small, focused commit │
│ 提交 小而聚焦的提交 │
│ ↓ │
│ Next slice / 下一个切片 │
└──────────────────────────────────────────┘
One slice at a time. One seam, one test, one minimal implementation per cycle. / 一次一个切片。每个循环一个边界、一个测试、最小实现。
A seam is the public boundary you test at — the interface where you observe behavior without reaching inside. Tests live at seams, never against internals. / **边界(seam)**是你测试的公共边界——你观察行为但不深入内部的接口。测试只在边界,绝不在内部。
Test only at pre-agreed seams. Before writing any test, write down the seams under test and confirm them. You can't test everything, so agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case. / 只在预先约定的边界测试。写任何测试之前,先列出要测的边界并确认。你不可能测所有,所以预先约定边界能让测试力量集中到关键路径和复杂逻辑上,而不是每个边界条件。
Ask first: "What's the public interface, and which seams should we test?" / 先问:「公共接口是什么?应该测哪些边界?」
Implementation-coupled / 实现耦合: mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed. / 模拟内部协作者、测私有方法、或通过旁路验证(直接查库而非走接口)。识别标志:重构时测试挂掉,但行为没变。
Tautological / 恒真断言: the assertion recomputes the expected value the way the code does (expect(add(a, b)).toBe(a + b), a snapshot derived by hand the same way, a constant asserted equal to itself). Expected values must come from an independent source of truth: a known-good literal, a worked example, the spec. / 断言用代码同样的方式重算期望值(expect(add(a, b)).toBe(a + b)、手工算的快照、常量断言等于自身)。期望值必须来自独立的真相源:已知的字面值、算好的示例、规范。
Horizontal slicing / 横向切片: writing all tests first, then all implementation. Bulk tests verify imagined behavior; tests go insensitive to real changes; you commit to test structure before understanding the implementation. Work in vertical slices instead: one test → one implementation → repeat, each test a tracer bullet that responds to what the last cycle taught you. / 先写所有测试,再写所有实现。批量测试验证的是「想象中的」行为;测试对真实变化不敏感;你在理解实现前就锁死了测试结构。改用纵向切片:一个测试 → 一个实现 → 重复,每个测试都是回应上次循环教你的示踪弹。
Speculative features / 投机功能: Don't anticipate future tests or add features "while I'm here." YAGNI. If you didn't write a failing test for it, don't build it. / 不要预想未来测试或「顺便」加功能。YAGNI(你不会需要它)。没写失败测试,就不要做。
See references/anti-patterns.md for full list with BAD/GOOD examples. / 完整反模式列表与正反例见 references/anti-patterns.md。
Works with any test runner. Pick one based on your stack: / 适用于任何测试运行器。按技术栈选择:
| Stack / 技术栈 | Test runner | Example assertion |
|---|---|---|
| JavaScript / TypeScript (Node) | Jest, Vitest, Mocha | expect(fn(x)).toBe(y) |
| Python | pytest, unittest | assert fn(x) == y |
| Go | built-in go test | if got := fn(x); got != y { t.Errorf(...) } |
| Rust | built-in cargo test | assert_eq!(fn(x), y) |
| Java / Kotlin | JUnit 5, Kotest | assertEquals(y, fn(x)) |
| C# | xUnit, NUnit | Assert.Equal(y, fn(x)) |
| Ruby | RSpec, Minitest | expect(fn(x)).to eq(y) |
If your project has no test framework yet, see examples/no-framework.md for the manual approach (use assert + a runner script). / 如果项目还没有测试框架,手测方法见 examples/no-framework.md(用 assert + 临时运行脚本)。
Mock at the seam, not inside the unit. If you find yourself mocking private methods, you're testing the wrong thing. / 在边界 mock,不要在单元内部 mock。如果你发现自己在 mock 私有方法,说明你测错了对象。
See references/mocking.md for: when to mock, when not to mock, dependency injection patterns, fake vs. stub vs. mock distinction. / 何时 mock、何时不 mock、依赖注入模式、fake/stub/mock 区别见 references/mocking.md。
See / 见:
slugify function / TypeScript + Vitest,slugify 函数完整红→绿→重构When you finish a TDD cycle, you have: / 完成 TDD 循环后,你拥有:
user can checkout with empty cart shows error) / 测试名像规范一样读You do NOT have: / 你没有:
If you find yourself: / 如果你发现自己在:
expect(add(a,b)).toBe(a+b) / 写 expect(add(a,b)).toBe(a+b) 这类恒真断言→ STOP. Return to RED. / 停止。回到红。
Before saying "the TDD cycle is complete": / 在说「TDD 循环完成」之前:
If any item is unchecked, the cycle isn't done. / 任何一项未勾,循环未完成。