supersam/docs/superpowers/skills/test-driven-development/SKILL.md

148 lines
3.8 KiB
Markdown

---
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.