|
1 | 1 | # Test Suite Implementation Plan |
2 | 2 |
|
| 3 | +## Testing Requirements |
| 4 | + |
| 5 | +**Unit tests are required for all features.** This is not optional. See [PRINCIPLES_AND_GOALS.md](PRINCIPLES_AND_GOALS.md#technical-principles-testability-and-maintainability) for why. |
| 6 | + |
| 7 | +### Core Rules |
| 8 | + |
| 9 | +1. **Every feature must have unit tests.** Before a feature is considered complete, it must have tests that verify its behavior. |
| 10 | + |
| 11 | +2. **Refactor over complex tests.** If writing a test requires: |
| 12 | + - Extensive mocking or setup |
| 13 | + - Deep knowledge of unrelated systems |
| 14 | + - More than a few lines of boilerplate |
| 15 | + |
| 16 | + ...then **refactor the code**, don't write a more complex test. Hard-to-test code is hard-to-maintain code. |
| 17 | + |
| 18 | +3. **Tests document behavior.** Tests show how code is meant to be used. If a test is hard to read, the API is probably hard to use. |
| 19 | + |
| 20 | +4. **Tests enable safe refactoring.** The test suite should give confidence that changes don't break existing behavior. If you can't refactor safely, add more tests first. |
| 21 | + |
| 22 | +### What to Test |
| 23 | + |
| 24 | +| Layer | What to test | Example | |
| 25 | +|-------|--------------|---------| |
| 26 | +| **Schemas** | Validation, factory functions, edge cases | `PersonaSchema.safeParse()`, `createPersona()` | |
| 27 | +| **Utilities** | Pure functions, transformations | `resolvePath()`, `findPersona()` | |
| 28 | +| **CLI commands** | Command execution, structured results | `executeCommandLine('ls')` returns expected result | |
| 29 | +| **Components** | Rendering, user interactions | ContactList renders items, handles selection | |
| 30 | + |
| 31 | +### Use Tests as a Design Guide |
| 32 | + |
| 33 | +**Unit tests are feedback on your design.** If code is hard to test, that's a signal the design is getting too complex or convoluted. Use this feedback: |
| 34 | + |
| 35 | +- Easy to test → probably easy to understand, change, and reuse |
| 36 | +- Hard to test → probably too coupled, doing too much, or poorly factored |
| 37 | + |
| 38 | +Write the test first (or at least think about how you'd test it) before the implementation gets complicated. If you can't imagine a simple test, simplify the design. |
| 39 | + |
| 40 | +### When Tests Are Hard to Write |
| 41 | + |
| 42 | +If you find yourself struggling to test something, ask: |
| 43 | + |
| 44 | +- **Is this unit doing too much?** Split it into smaller pieces. |
| 45 | +- **Are there too many dependencies?** Inject them or extract pure logic. |
| 46 | +- **Is the state management tangled?** Separate state from behavior. |
| 47 | +- **Am I testing implementation details?** Test behavior, not internals. |
| 48 | + |
| 49 | +The answer is almost always "refactor the code" rather than "write a more elaborate test." |
| 50 | + |
| 51 | +### Exception: Performance-Critical Code |
| 52 | + |
| 53 | +Code optimized for performance may have a structure that's harder to test (e.g., inlined logic, avoiding allocations). This is acceptable **only if**: |
| 54 | + |
| 55 | +1. **Explicitly marked** — Comment explains why this code is performance-critical |
| 56 | +2. **Well bounded** — Small and focused on one thing |
| 57 | +3. **Isolated** — Minimal dependencies, clear interface |
| 58 | + |
| 59 | +Such code still needs tests, but they may test at the boundary (inputs → outputs) rather than internals. The isolation requirement ensures that hard-to-test performance code doesn't spread throughout the codebase. |
| 60 | + |
| 61 | +--- |
| 62 | + |
3 | 63 | ## Status: Complete |
4 | 64 |
|
5 | 65 | ### Completed |
|
0 commit comments