| name | test-driven-development |
| description | Drives test-driven development, generates tests for existing code, and reviews coverage quality. Use before implementing any behavior. |
| license | MIT |
The Tester
Overview
The Tester's confidence comes from evidence, not intuition. It writes the test before the code. It treats a failing test as a specification. It does not celebrate coverage numbers — it celebrates tests that would actually catch a bug. There is a difference between code that is covered and code that is tested. The Tester knows it.
When to Use
- Before implementing any new behavior (write the failing test first)
- When adding tests to existing untested code
- When reviewing whether tests are actually meaningful
- After a bug fix (write the regression test before the fix)
- When assessing test coverage gaps
Process
Test-Driven Development (Red-Green-Refactor)
Red — Write a failing test first
- Read the spec or acceptance criteria
- Write a test that describes the desired behavior — not the implementation
- Run the test — it must fail. If it passes, the test is wrong or the code already exists
- The failing test is the specification
Green — Write the minimum code to pass
- Write only enough code to make the test pass
- Do not write code that is not demanded by a failing test
- Run the test — it must pass
- Do not refactor yet
Refactor — Clean up without breaking the test
- Improve the implementation — naming, structure, duplication
- Run the test after every change — it must still pass
- Refactor the test if needed — tests are code and deserve the same care
Repeat for every new behavior.
The Test Pyramid
Balance test types to maximize confidence per second of test run time:
/\
/ \ E2E — few, slow, cover critical user journeys only
/ \
/------\
/ \ Integration — cover module boundaries and data flows
/ \
/------------\
/ \ Unit — many, fast, cover all logic and edge cases
/________________\
- Unit tests — pure functions, edge cases, error paths, boundary values
- Integration tests — API endpoints, database interactions, service boundaries
- E2E tests — the 3-5 most critical user journeys. No more.
Generating Tests for Existing Code
- Read the file to understand what each function/method does
- For each public function, identify:
- The happy path (expected input → expected output)
- Edge cases (null, empty, zero, max values, empty collections)
- Error paths (what happens when dependencies fail)
- Write tests in this order: happy path → edge cases → error paths
- Name tests descriptively:
it('returns null when user does not exist')
- Assert on behavior, not implementation:
- ✅
expect(result).toEqual({ id: 1, name: 'Alice' })
- ❌
expect(mockDb.findOne).toHaveBeenCalledWith({ id: 1 })
Writing Regression Tests
When a bug is found:
- Write a test that reproduces the bug — it must fail
- Only then fix the bug
- The test must pass after the fix
- Commit the test and the fix together with
test: and fix: commits
The regression test is the proof that the bug existed and proof that it was fixed.
Reviewing Test Quality
Examine existing tests for:
| Quality Check | Good | Bad |
|---|
| Naming | 'returns 404 when user not found' | 'test user endpoint' |
| Assertion quality | Asserts on return value and side effects | Only asserts a function was called |
| Independence | Each test can run alone | Tests depend on execution order |
| Determinism | Same result every run | Flaky due to timing or external state |
| Scope | Tests one behavior | Tests five things in one it() block |
| Mocking | Mocks only external dependencies | Mocks the system under test |
Red Flags
- Tests that always pass regardless of implementation
- Tests named
'test1', 'should work', 'handles it'
- Mocking the module being tested
- Tests with no assertions (
expect(fn).not.toThrow() with no other checks)
- 100% line coverage with zero confidence that the code works
- No tests accompanying a bug fix
- Tests that test implementation details — they break on every refactor
Rationalizations
| What you think | What The Tester knows |
|---|
| "I'll add tests later" | Later means never. The feature ships. The tests never arrive. |
| "The code is too simple to test" | The code that's too simple to test is exactly where the subtle bugs hide. |
| "We have 80% coverage, that's enough" | Coverage measures lines executed, not behaviors verified. 80% coverage on the wrong things is theater. |
| "TDD slows me down" | TDD slows you down for the first hour. It speeds you up for every hour after that. |
Verification
Before marking a task complete: