--- name: test-driven-development description: Use when implementing any feature or bugfix, before writing implementation code --- # Test-Driven Development (TDD) ## Overview Write the test first. Watch it fail. Write minimal code to pass. **Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing. ## When to Use **Always:** - New features - Bug fixes - Refactoring - Behavior changes **Exceptions (ask your human partner):** - Throwaway prototypes - Generated code - Configuration files ## The Iron Law ``` NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST ``` Write code before the test? Delete it. Start over. ## Red-Green-Refactor ### RED — Write Failing Test Write one minimal test showing what should happen. ```javascript test('retries failed operations 3 times', async () => { let attempts = 0; const operation = () => { attempts++; if (attempts < 3) throw new Error('fail'); return 'success'; }; const result = await retryOperation(operation); expect(result).toBe('success'); expect(attempts).toBe(3); }); ``` **Requirements:** - One behavior - Clear name - Real code (no mocks unless unavoidable) ### Verify RED — Watch It Fail **MANDATORY. Never skip.** ```bash npm test -- path/to/test.test.js ``` Confirm: - Test fails (not errors) - Failure message is expected - Fails because feature missing (not typos) **Test passes?** You're testing existing behavior. Fix test. **Test errors?** Fix error, re-run until it fails correctly. ### GREEN — Minimal Code Write simplest code to pass the test. Don't add features, refactor other code, or "improve" beyond the test. ### Verify GREEN — Watch It Pass **MANDATORY.** ```bash npm test -- path/to/test.test.js ``` Confirm: - Test passes - Other tests still pass - Output pristine (no errors, warnings) **Test fails?** Fix code, not test. **Other tests fail?** Fix now. ### REFACTOR — Clean Up After green only: - Remove duplication - Improve names - Extract helpers Keep tests green. Don't add behavior. ### Repeat Next failing test for next feature. ## This Project's Test Conventions - Test files live alongside source: `src/services/orderService.test.js` - Test runner: **Vitest** (`npm run test`) - Tests are `.test.js` files, co-located with the module they test - Existing tested modules: `orderService.js`, `deliveryInvitationApi.js`, `driverDeliveries.js`, `orderViews.js` - Mock data available in `src/data/mockAppData.js` for test fixtures ## Common Rationalizations | Excuse | Reality | |--------|---------| | "Too simple to test" | Simple code breaks. Test takes 30 seconds. | | "I'll test after" | Tests passing immediately prove nothing. | | "Already manually tested" | Ad-hoc ≠ systematic. No record, can't re-run. | | "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. | | "TDD is dogmatic, I'm being pragmatic" | TDD IS pragmatic: finds bugs before commit, prevents regressions. | ## Red Flags — STOP and Start Over - Code before test - Test passes immediately - Test after implementation - "I already manually tested it" - "This is different because..." **All of these mean: Delete code. Start over with TDD.** ## Verification Checklist Before marking work complete: - [ ] Every new function/method has a test - [ ] Watched each test fail before implementing - [ ] Each test failed for expected reason (feature missing, not typo) - [ ] Wrote minimal code to pass each test - [ ] All tests pass - [ ] Output pristine (no errors, warnings) - [ ] Tests use real code (mocks only if unavoidable) - [ ] Edge cases and errors covered Can't check all boxes? You skipped TDD. Start over. ## Final Rule ``` Production code → test exists and failed first Otherwise → not TDD ``` No exceptions without your human partner's permission.