Testing Strategy

The module structure enables testing by separating concerns.

Testing Pyramid

         ╱╲
        ╱  ╲         E2E Tests (Playwright)
       ╱ 5% ╲        Full user flows, critical paths only
      ╱──────╲
     ╱        ╲      Integration Tests
    ╱   15%    ╲     Module pages with mocked APIs
   ╱────────────╲
  ╱              ╲   Unit Tests
 ╱      80%       ╲  Hooks, utils, components in isolation
╱──────────────────╲

What Gets Tested Where

LayerWhat to TestHow
utils/Pure functionsUnit tests - input/output
hooks/State logic, API callsUnit tests with mocked fetch
components/Rendering, interactionsComponent tests (Testing Library)
modules/*/pages/Composition, error statesIntegration tests
rendering/server/SSR logicUnit tests (no browser needed)
Full flowsCritical user journeysE2E (Playwright)

Test File Convention

Tests live next to the code they test:

modules/stories/
├── components/
│   ├── StoryCard.tsx
│   └── StoryCard.test.tsx
├── hooks/
│   ├── useFetchStory.ts
│   └── useFetchStory.test.ts
└── pages/
    ├── StoryPage.tsx
    └── StoryPage.test.tsx

Why This Structure Enables Testing

  1. SSR logic is extractable - rendering/server/ can be unit tested without Next.js
  2. Hooks are isolated - Test data fetching without rendering components
  3. Components receive props - No internal fetching = easy to test with mock data
  4. Modules have boundaries - Test a module in isolation by mocking dependencies