# test-driven-development > When implementing any feature or bugfix - write the test first, watch it fail, write minimal code to pass; if you didn't watch the test fail, you don't know if it tests the right thing - Author: jglaspey - Repository: jglaspey/GiveJasonFeedbackWidget - Version: 20251219101309 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-07 - Source: https://github.com/jglaspey/GiveJasonFeedbackWidget - Web: https://mule.run/skillshub/@@jglaspey/GiveJasonFeedbackWidget~test-driven-development:20251219101309 --- --- name: test-driven-development description: When implementing any feature or bugfix - write the test first, watch it fail, write minimal code to pass; if you didn't watch the test fail, you don't know if it tests the right thing --- # 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. **Violating the letter of the rules is violating the spirit of the rules.** ## The Iron Law ``` NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST ``` Write code before the test? Delete it. Start over. **No exceptions:** - Don't keep it as "reference" - Don't "adapt" it while writing tests - Delete means delete ## When to Use **Always:** - New features - Bug fixes - Refactoring - Behavior changes **Exceptions (ask your partner):** - Throwaway prototypes - Generated code - Configuration files Thinking "skip TDD just this once"? Stop. That's rationalization. ## Red-Green-Refactor ### RED - Write Failing Test Write one minimal test showing what should happen. ```typescript 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 per test - Clear name describing the behavior - Real code (no mocks unless unavoidable) ### Verify RED - Watch It Fail **MANDATORY. Never skip.** ```bash npm test path/to/test.test.ts ``` Confirm: - Test fails (not errors) - Failure message is expected - Fails because feature missing (not typos) **Test passes?** You're testing existing behavior. Fix test. ### GREEN - Minimal Code Write simplest code to pass the test. Don't add features, refactor other code, or "improve" beyond the test. ```typescript async function retryOperation(fn: () => Promise): Promise { for (let i = 0; i < 3; i++) { try { return await fn(); } catch (e) { if (i === 2) throw e; } } throw new Error('unreachable'); } ``` ### Verify GREEN - Watch It Pass **MANDATORY.** ```bash npm test path/to/test.test.ts ``` Confirm: - Test passes - Other tests still pass - Output pristine (no errors, warnings) ### REFACTOR - Clean Up After green only: remove duplication, improve names, extract helpers. Keep tests green. Don't add behavior. ## 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. | | "TDD will slow me down" | TDD faster than debugging. | | "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. | ## Red Flags - STOP and Start Over - Code before test - Test passes immediately - Tests added "later" - "Just this once" - "Already manually tested it" - "Keep as reference" **All of these mean: Delete code. Start over with TDD.** ## Example: Bug Fix **Bug:** Empty email accepted **RED** ```typescript test('rejects empty email', async () => { const result = await submitForm({ email: '' }); expect(result.error).toBe('Email required'); }); ``` **Verify RED** - see it fail **GREEN** ```typescript function submitForm(data: FormData) { if (!data.email?.trim()) { return { error: 'Email required' }; } // ... } ``` **Verify GREEN** - all tests pass ## Verification Checklist Before marking work complete: - [ ] Every new function has a test - [ ] Watched each test fail before implementing - [ ] Wrote minimal code to pass each test - [ ] All tests pass - [ ] Output pristine (no errors, warnings) Can't check all boxes? You skipped TDD. Start over. ## The Bottom Line ``` Production code → test exists and failed first Otherwise → not TDD ``` No exceptions without your partner's permission.