Testing Strategy
Unit, integration, e2e -- when to use each; doubles, fixtures, naming.
testingquality
# Testing Strategy
## The pyramid
- **Unit** (many, fast, ms): pure functions, single module. No I/O.
- **Integration** (some, seconds): module + DB / HTTP / FS. Real boundaries with test infra.
- **End-to-end** (few, slow, minutes): full stack through UI/API.
## Naming
`describe('Cart')` / `it('applies discount when coupon valid')`
Pattern: `<unit>_<scenario>_<expected>` or BDD `Given/When/Then`.
## Arrange-Act-Assert
```ts
test('subtract', () => {
// Arrange
const a = 5, b = 2
// Act
const r = sub(a, b)
// Assert
expect(r).toBe(3)
})
```
## Test doubles
- **Stub**: returns canned data (no behavior verification).
- **Mock**: verifies interaction (`toHaveBeenCalledWith`).
- **Fake**: working light implementation (in-memory DB).
- **Spy**: wraps real impl + records calls.
Prefer fakes > stubs > mocks for resilience.
## Fixtures
- Build helpers, not raw data: `makeUser({ email: 'x' })`.
- Reset state between tests; use transactions for DB.
## What to test
- Public behavior, not implementation details.
- Boundary conditions, error paths.
- Don't test framework code or trivial getters.
## E2E (Playwright)
```ts
test('login', async ({ page }) => {
await page.goto('/login')
await page.getByLabel('Email').fill('a@b.c')
await page.getByLabel('Password').fill('pw')
await page.getByRole('button', { name: 'Sign in' }).click()
await expect(page).toHaveURL('/dashboard')
})
```
API: /api/skills/testing-pyramid