tdd
GitHub指导使用测试驱动开发(红绿重构)流程进行代码变更,确保先写失败测试再实现功能。适用于Bug修复、新功能及重构,涵盖Go/TS单元测试及E2E集成规范。
Trigger Scenarios
Install
npx skills add kdlbs/kandev --skill tdd -g -y
SKILL.md
Frontmatter
{
"name": "tdd",
"description": "Implement changes using Test-Driven Development (Red-Green-Refactor). Use for bug fixes, new features, or any code change that should have test coverage."
}
TDD
Execution Context
Use this procedure directly in the primary conversation for every code change, regardless of size. The user may switch that conversation to the lower-cost implementation model before beginning the Red-Green-Refactor cycle.
Implement code changes using strict Red-Green-Refactor. Iron law: no production code without a failing test first.
Wrote code before a test? Delete it. Start over from a failing test.
Related skills
/e2e— Follow this procedure when the current task needs Playwright E2E coverage./pr-fixup— After the task-defined checks pass and the PR opens, use it only for CI or actionable reviewer findings.
When to use
- Bug fixes — write a test that reproduces the bug before fixing
- New functions, methods, or utilities
- Refactoring existing logic that lacks tests
Skip for: pure UI components (we don't test React components), config files, generated code.
For UI rendering bugs, prefer extracting or using a pure helper and testing that helper. Add Playwright only when the behavior is truly visual or integration-level. Avoid adding React component tests just to assert DOM output; that does not match this project's testing convention.
Determine test scope
- Go unit (
apps/backend/): test file next to source as*_test.go. Run:cd apps/backend && go test -v -run TestName ./internal/path/to/package/... - TypeScript unit (
apps/web/lib/): test file next to source as*.test.ts. Run:cd apps && pnpm --filter @kandev/web test -- --run path/to/file.test.ts - Web E2E (
apps/web/e2e/): follow/e2ewhen the current task needs Playwright tests.
Choose the right level:
- Unit: pure logic or isolated service behavior.
- Integration: handler/service/repository boundaries, SQLite-backed flows, filesystem behavior, or process boundaries.
- E2E: critical user-facing browser flows; keep these focused and use
/e2e.
Prefer state/output assertions over interaction assertions. Mock only slow, nondeterministic, or external boundaries; use real implementations or fakes when they keep the test deterministic.
For failure-path tests, inject the error at the boundary the production code claims to handle and exercise the real downstream call chain; do not short-circuit by mocking the handler under test.
Cross-layer contracts
When adding or renaming a field that crosses backend, WebSocket, and frontend
boundaries, use rg to trace every producer, DTO, store upsert or partial
merge, reconnect/readiness handler, and consumer before editing. Add focused
coverage at the affected boundaries, including refresh/reconnect and an update
that omits the field, so a partial payload cannot silently discard an existing
value.
Concurrent and event-driven behavior
Test ordering-sensitive behavior with channels, barriers, or controllable fakes;
do not use sleeps to create a race, except for a bounded, named delay that
models a known poll-loop schedule when synchronization would alter that
relationship. Pause at the ownership boundary, start the
competing operation, then release. Exercise the real delivery path where
practical, and prove the old interleaving fails before the fix. Assert both the
winner state and the untouched replacement state, including relevant buffers,
signals, or queue ownership. Cover stale events acting after a replacement
operation begins, cancellation/retry ownership, and at-most-once delivery when
they apply. Run affected Go packages with -race.
When delayed state has a lifecycle owner, pair the boundary tests: disposal before the threshold must cancel timers and emit nothing later; disposal or replacement after publication must immediately clear externally observable state; and stale callbacks must not mutate the replacement.
Steps
1. RED — Write a failing test
- Identify the single behavior to implement or bug to reproduce
- Write the smallest test that asserts the expected behavior — one assertion, clear name
- Run the test and confirm it fails with the expected assertion error (not a compile/import error) For a brand-new Go package, create the package directory and minimal test package first, then run the focused package test so RED fails on behavior rather than package-selection or import errors.
- If it passes immediately, the test is not testing new behavior — revise it. Exception: a reviewer-requested test that documents behavior already present on the current head is valid test-only contract coverage. Label it as such, make no production change, and run the focused suite.
For bug fixes, use the Prove-It Pattern: reproduce the bug with a failing test before changing production code. A fix without a regression test is not complete unless the change is explicitly untestable and you say why.
2. GREEN — Minimal code to pass
- Write the minimum production code to make the failing test pass
- Do not add extra logic, handle other edge cases, or refactor yet
- Run the test again and confirm it passes
- If it fails, fix the production code (not the test)
3. REFACTOR — Clean up
- Improve production code: extract helpers, rename, simplify — without changing behavior
- Improve tests: table-driven tests (Go) or
describe/itblocks (TS), remove duplication - Run the test after each change to confirm still green
In tests, prefer DAMP over DRY: each test should read like a small specification. Shared helpers are fine when they remove noise, but not when they hide the scenario.
4. Repeat
Return to step 1 for the next behavior or edge case. Continue until the feature or fix is complete.
5. Final verification
Run the targeted tests named in the task file and report their results. Commit and open the PR after all affected task checks pass; do not add broad local verification by default.
After the final production-code edit, rerun every new or changed regression test and report the exact command and result. A prior green run does not cover a later patch.
Testing anti-patterns
Don't test implementation details:
- Assert behavior, state, API response, DB row, emitted event, or UI outcome. Avoid assertions that only prove a helper was called or an internal query string happened to be built a certain way.
- Custom hooks and async controller helpers are behavior-bearing logic, not pure React markup: add focused tests for success, failure, cancellation/no-op, and busy/loading-state cleanup.
Don't test mock behavior:
- If your assertion checks a mock element (
*-mocktest ID, mock return value), you're testing the mock, not the code. Test real behavior or don't mock it.
Don't add test-only methods to production code:
destroy(),reset(),_testHelper()that only tests call — put these in test utilities, not production classes.
Mock minimally and understand dependencies:
- Before mocking, ask: what side effects does the real method have? Does the test depend on any of them?
- Mock the slow/external part (network, disk), not the method the test depends on.
- If mock setup is longer than test logic, consider an integration test instead.
Don't use incomplete mocks:
- Mock the complete data structure as it exists in reality, not just fields your test uses. Partial mocks hide bugs when downstream code accesses omitted fields.
- When adding a named export to a shared module, search for full-module
vi.mock()factories and add the export to each factory. Focused tests can pass while a full suite fails on an out-of-date module shape.
Never swallow errors in tests:
try/catchthat silently ignores failures in test helpers or setup — these hide real failures.
Don't repeat unchanged passing commands for reassurance:
- After a clean targeted run, re-run only after code or test inputs change. Move to the next required verification step instead.
Red flags
- Writing production code before a failing test exists — delete and start over
- Test passes on first run — revise it, except for clearly labelled reviewer-requested test-only contract coverage
- Fixing a test to make it pass instead of fixing the production code
- Large jumps — multiple behaviors implemented between test runs
- Skipping the refactor step
- Mock setup longer than test logic — consider integration test
- Asserting on mock elements instead of real behavior
- "All tests pass" but no relevant test was actually run
Version History
-
1578843
Current 2026-08-16 08:48
移除对planner/worker的代理委派描述,改为直接在主对话执行;更新子技能列表,将/verify替换为/pr-fixup并调整触发时机说明。
- b4239d8 2026-07-24 17:33


