3.8 KiB
| name | description |
|---|---|
| test-driven-development | 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.
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.
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.
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.jsfiles, 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.jsfor 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.