--- name: writing-tests description: Writes behavior-focused tests using Testing Trophy model with real dependencies. Use when writing tests, choosing test types, or avoiding anti-patterns like testing mocks. --- If the current repo has its own rules/skills covering this topic (check .claude/rules/ and repo CLAUDE.md), those take precedence — apply this skill only where they're silent. # Writing Tests **Core principle:** Test user-observable behavior with real dependencies. Tests should survive refactoring. > "The more your tests resemble the way your software is used, the more confidence they can give you." — Kent C. Dodds **Why this matters:** Tests exist to give you confidence. The Testing Trophy prioritizes integration tests because they test real behavior across real modules — giving maximum confidence per test written. Unit tests in isolation often just test mocks, not your actual system. ## Testing Trophy Model | Priority | Type | When | | -------- | ----------- | ----------------------------------------------- | | 1st | Integration | Default - multiple units with real dependencies | | 2nd | E2E | Complete user workflows | | 3rd | Unit | Pure functions only (no dependencies) | ## Mocking Guidelines **Default: Don't mock. Use real dependencies.** **Only mock:** - External HTTP/API calls - Time/randomness - Third-party services (payments, email) **Never mock:** - Internal modules - Database queries (use test DB) - Business logic - Your own code calling your own code **Before mocking, ask:** "What side effects does this have? Does my test need those?" If unsure, run with real implementation first, then add minimal mocking only where needed. ## Test Type Decision ``` Complete user workflow? → E2E test Pure function (no side effects)? → Unit test Everything else → Integration test ``` ## Assertion Strategy | Context | Assert On | Avoid | | ------- | --------------------- | --------------------------- | | UI | Visible text, roles | CSS classes, internal state | | API | Response body, status | Internal DB state | | Library | Return values | Private methods | ## Anti-Patterns | Pattern | Fix | | ------------------------------- | --------------------------- | | Testing mock calls | Test actual outcome | | Test-only methods in production | Move to test utilities | | `sleep(500)` | Poll for actual condition | | Asserting on internal state | Assert on observable output | | Incomplete mocks | Mirror real API completely | ## Quality Checklist - [ ] Happy path covered - [ ] Error conditions handled - [ ] Real dependencies used (minimal mocking) - [ ] Tests survive refactoring - [ ] Test names describe behavior ## Language-Specific Patterns - **JavaScript/Typescript/React**: See [references/typescript-react.md](references/typescript-react.md) - **Python**: See [references/python.md](references/python.md) - **Go**: See [references/go.md](references/go.md) ## Async Waiting Wait for the actual condition, not a guess about how long it takes. ```typescript // Bad: arbitrary delay await new Promise(r => setTimeout(r, 2000)); expect(element).toBeVisible(); // Good: poll for condition await waitFor(() => expect(element).toBeVisible()); ``` **Prefer framework built-ins:** - Testing Library: `findBy` queries, `waitFor` - Playwright: auto-waiting, `expect(locator).toBeVisible()` - pytest: `asyncio.wait_for`, tenacity **Language-specific waiting patterns:** - **TypeScript**: See [references/waiting-typescript.md](references/waiting-typescript.md) - **Python**: See [references/waiting-python.md](references/waiting-python.md) --- **Remember:** Behavior over implementation. Real over mocked. Outputs over internals.