$npx -y skills add jamditis/claude-skills-journalism --skill test-driven-developmentUse when implementing any feature or bugfix, before writing implementation code
| 1 | <!-- |
| 2 | Adapted from obra/superpowers test-driven-development skill (v5.0.7), |
| 3 | MIT-licensed, copyright 2025 Jesse Vincent. Modifications copyright 2026 Joe Amditis. v0.3.0 ports as a consumer category — no research |
| 4 | phase per the v0.2.0 architecture, since TDD is a sub-skill called |
| 5 | within other workflows whose specs/plans already encode the research |
| 6 | conclusions. The artifact handoff carries those conclusions. |
| 7 | See CREDITS.md. |
| 8 | --> |
| 9 | |
| 10 | # Test-Driven Development (TDD) |
| 11 | |
| 12 | ## Overview |
| 13 | |
| 14 | Write the test first. Watch it fail. Write minimal code to pass. |
| 15 | |
| 16 | **Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing. |
| 17 | |
| 18 | **Violating the letter of the rules is violating the spirit of the rules.** |
| 19 | |
| 20 | ## When to Use |
| 21 | |
| 22 | **Always:** |
| 23 | - New features |
| 24 | - Bug fixes |
| 25 | - Refactoring |
| 26 | - Behavior changes |
| 27 | |
| 28 | **Exceptions (ask your human partner):** |
| 29 | - Throwaway prototypes |
| 30 | - Generated code |
| 31 | - Configuration files |
| 32 | |
| 33 | Thinking "skip TDD just this once"? Stop. That's rationalization. |
| 34 | |
| 35 | ## The Iron Law |
| 36 | |
| 37 | ``` |
| 38 | NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST |
| 39 | ``` |
| 40 | |
| 41 | Write code before the test? Delete it. Start over. |
| 42 | |
| 43 | **No exceptions:** |
| 44 | - Don't keep it as "reference" |
| 45 | - Don't "adapt" it while writing tests |
| 46 | - Don't look at it |
| 47 | - Delete means delete |
| 48 | |
| 49 | Implement fresh from tests. Period. |
| 50 | |
| 51 | ## Red-Green-Refactor |
| 52 | |
| 53 | ```dot |
| 54 | digraph tdd_cycle { |
| 55 | rankdir=LR; |
| 56 | red [label="RED\nWrite failing test", shape=box, style=filled, fillcolor="#ffcccc"]; |
| 57 | verify_red [label="Verify fails\ncorrectly", shape=diamond]; |
| 58 | green [label="GREEN\nMinimal code", shape=box, style=filled, fillcolor="#ccffcc"]; |
| 59 | verify_green [label="Verify passes\nAll green", shape=diamond]; |
| 60 | refactor [label="REFACTOR\nClean up", shape=box, style=filled, fillcolor="#ccccff"]; |
| 61 | next [label="Next", shape=ellipse]; |
| 62 | |
| 63 | red -> verify_red; |
| 64 | verify_red -> green [label="yes"]; |
| 65 | verify_red -> red [label="wrong\nfailure"]; |
| 66 | green -> verify_green; |
| 67 | verify_green -> refactor [label="yes"]; |
| 68 | verify_green -> green [label="no"]; |
| 69 | refactor -> verify_green [label="stay\ngreen"]; |
| 70 | verify_green -> next; |
| 71 | next -> red; |
| 72 | } |
| 73 | ``` |
| 74 | |
| 75 | ### RED - Write Failing Test |
| 76 | |
| 77 | Write one minimal test showing what should happen. |
| 78 | |
| 79 | <Good> |
| 80 | ```typescript |
| 81 | test('retries failed operations 3 times', async () => { |
| 82 | let attempts = 0; |
| 83 | const operation = () => { |
| 84 | attempts++; |
| 85 | if (attempts < 3) throw new Error('fail'); |
| 86 | return 'success'; |
| 87 | }; |
| 88 | |
| 89 | const result = await retryOperation(operation); |
| 90 | |
| 91 | expect(result).toBe('success'); |
| 92 | expect(attempts).toBe(3); |
| 93 | }); |
| 94 | ``` |
| 95 | Clear name, tests real behavior, one thing |
| 96 | </Good> |
| 97 | |
| 98 | <Bad> |
| 99 | ```typescript |
| 100 | test('retry works', async () => { |
| 101 | const mock = jest.fn() |
| 102 | .mockRejectedValueOnce(new Error()) |
| 103 | .mockRejectedValueOnce(new Error()) |
| 104 | .mockResolvedValueOnce('success'); |
| 105 | await retryOperation(mock); |
| 106 | expect(mock).toHaveBeenCalledTimes(3); |
| 107 | }); |
| 108 | ``` |
| 109 | Vague name, tests mock not code |
| 110 | </Bad> |
| 111 | |
| 112 | **Requirements:** |
| 113 | - One behavior |
| 114 | - Clear name |
| 115 | - Real code (no mocks unless unavoidable) |
| 116 | |
| 117 | ### Verify RED - Watch It Fail |
| 118 | |
| 119 | **MANDATORY. Never skip.** |
| 120 | |
| 121 | ```bash |
| 122 | npm test path/to/test.test.ts |
| 123 | ``` |
| 124 | |
| 125 | Confirm: |
| 126 | - Test fails (not errors) |
| 127 | - Failure message is expected |
| 128 | - Fails because feature missing (not typos) |
| 129 | |
| 130 | **Test passes?** You're testing existing behavior. Fix test. |
| 131 | |
| 132 | **Test errors?** Fix error, re-run until it fails correctly. |
| 133 | |
| 134 | ### GREEN - Minimal Code |
| 135 | |
| 136 | Write simplest code to pass the test. |
| 137 | |
| 138 | <Good> |
| 139 | ```typescript |
| 140 | async function retryOperation<T>(fn: () => Promise<T>): Promise<T> { |
| 141 | for (let i = 0; i < 3; i++) { |
| 142 | try { |
| 143 | return await fn(); |
| 144 | } catch (e) { |
| 145 | if (i === 2) throw e; |
| 146 | } |
| 147 | } |
| 148 | throw new Error('unreachable'); |
| 149 | } |
| 150 | ``` |
| 151 | Just enough to pass |
| 152 | </Good> |
| 153 | |
| 154 | <Bad> |
| 155 | ```typescript |
| 156 | async function retryOperation<T>( |
| 157 | fn: () => Promise<T>, |
| 158 | options?: { |
| 159 | maxRetries?: number; |
| 160 | backoff?: 'linear' | 'exponential'; |
| 161 | onRetry?: (attempt: number) => void; |
| 162 | } |
| 163 | ): Promise<T> { |
| 164 | // YAGNI |
| 165 | } |
| 166 | ``` |
| 167 | Over-engineered |
| 168 | </Bad> |
| 169 | |
| 170 | Don't add features, refactor other code, or "improve" beyond the test. |
| 171 | |
| 172 | ### Verify GREEN - Watch It Pass |
| 173 | |
| 174 | **MANDATORY.** |
| 175 | |
| 176 | ```bash |
| 177 | npm test path/to/test.test.ts |
| 178 | ``` |
| 179 | |
| 180 | Confirm: |
| 181 | - Test passes |
| 182 | - Other tests still pass |
| 183 | - Output pristine (no errors, warnings) |
| 184 | |
| 185 | **Test fails?** Fix code, not test. |
| 186 | |
| 187 | **Other tests fail?** Fix now. |
| 188 | |
| 189 | ### REFACTOR - Clean Up |
| 190 | |
| 191 | After green only: |
| 192 | - Remove duplication |
| 193 | - Improve names |
| 194 | - Extract helpers |
| 195 | |
| 196 | Keep tests green. Don't add behavior. |
| 197 | |
| 198 | ### Repeat |
| 199 | |
| 200 | Next failing test for next feature. |
| 201 | |
| 202 | ## Good Tests |
| 203 | |
| 204 | | Quality | Good | Bad | |
| 205 | |---------|------|-----| |
| 206 | | **Minimal** | One thing. "and" in name? Split it. | `test('validates email and domain and whitespace')` | |
| 207 | | **Clear** | Name describes behavior | `test('test1')` | |
| 208 | | **Shows intent** | Demonstrates desired API | Obscures what code should do | |
| 209 | |
| 210 | ## Why Order Matters |
| 211 | |
| 212 | **"I'll write tests after to verify it works"** |
| 213 | |
| 214 | Tests written after code pass immediately. Passing immedi |