Skip to main content

testing

Testing patterns - TDD workflow, Vitest unit tests, Playwright E2E, mock strategies, and coverage targets. Use when writing tests, setting up test infrastructure, following TDD, or diagnosing test failures.

ソース情報

リポジトリ
greenpill-dev-guild/coop
ソースの最終更新活動
2026年5月17日 03:22
検出された SKILL.md の言語
英語
スター
3
フォーク
1

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
4 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
testing
description
Testing patterns - TDD workflow, Vitest unit tests, Playwright E2E, mock strategies, and coverage targets. Use when writing tests, setting up test infrastructure, following TDD, or diagnosing test failures.
version
1.0.0
status
active
packages
["shared","app","extension"]
dependencies
[]
last_updated
2026-05-17
last_verified
2026-05-17
# Testing Skill Complete testing guide: TDD workflow, unit testing with Vitest, E2E testing with Playwright. --- ## Activation When invoked: - Decide the test type (unit/integration/E2E) based on impact and speed. - Locate existing test utilities in `packages/shared/src/__tests__/test-utils/`. - Prefer TDD for new behavior (RED -> GREEN -> REFACTOR). ## Part 1: Test-Driven Development (TDD) ### The Iron Law ``` NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST ``` Write code before the test? Delete it. Start over. No exceptions. ### When to Use TDD **Always:** - New features - Bug fixes - Refactoring - Behavior changes **Exceptions (ask your human partner):** - Throwaway prototypes - Generated code - Configuration files ### Red-Green-Refactor #### RED - Write Failing Test Write one minimal test showing what should happen: ```typescript // Good: Clear name, tests real behavior, one thing test('retries failed operations 3 times', async () => { let attempts = 0; const operation = () => { attempts++; if (attempts < 3) throw new Error('fail'); return 'success'; }; const result = await retryOperation(operation); expect(result).toBe('success'); expect(attempts).toBe(3); }); ``` #### Verify RED - Watch It Fail **MANDATORY. Never skip.** ```bash bun run test -- path/to/test.test.ts ``` > **CRITICAL: `bun test` vs `bun run test`** -- `bun test` invokes bun's built-in test runner which **ignores vitest config** (no jsdom, no aliases, no setup files). `bun run test` runs the package.json `"test"` script (vitest). **Always use `bun run test`** for vitest-based packages (shared, app, extension). To run a single test file: `bun run test -- path/to/test.test.ts`. Confirm: - Test fails (not errors) - Failure message is expected - Fails because feature missing (not typos) #### GREEN - Minimal Code Write simplest code to pass the test: ```typescript // Good: Just enough to pass async function retryOperation<T>(fn: () => Promise<T>): Promise<T> { let lastError: unknown; for (let i = 0; i < 3; i++) { try { return await fn(); } catch (e) { lastError = e; } } throw lastError; } ``` #### Verify GREEN - Watch It Pass **MANDATORY.** ```bash bun run test -- path/to/test.test.ts ``` #### REFACTOR - Clean Up After green only: remove duplication, improve names, extract helpers. Keep tests green. Don't add behavior. ### Common Rationalizations | Excuse | Reality | |--------|---------| | "Too simple to test" | Simple code breaks. Test takes 30 seconds. | | "I'll test after" | Tests passing immediately prove nothing. | | "Already manually tested" | Ad-hoc != systematic. No record, can't re-run. | | "TDD will slow me down" | TDD faster than debugging. | ### Bug Fix Workflow ``` 1. Write failing test that reproduces the bug 2. Run test -> watch it fail (confirms bug exists) 3. Fix the bug with minimal code 4. Run test -> watch it pass 5. Test now prevents regression forever ``` **Never fix bugs without a test.** --- ## Part 2: Unit Testing with Vitest ### Why Vitest - **Vite-native**: Same transformation pipeline as your app - **Fast HMR**: Tests rerun only on affected changes - **Jest-compatible**: Familiar API - **TypeScript native**: No extra config ### Core API ```typescript import { describe, it, test, expect } from "vitest"; // Basic test test("adds numbers", () => { expect(1 + 1).toBe(2); }); // With describe grouping describe("CoopService", () => { it("creates a coop with Safe address", async () => { const coop = await createCoop(testConfig); expect(coop.safeAddress).toBeDefined(); }); it.skip("skipped test", () => {}); it.only("focused test", () => {}); }); ``` ### Assertions ```typescript // Equality expect(value).toBe(exact); // === comparison expect(value).toEqual(deep); // Deep equality expect(value).toMatchObject(partial); // Partial match // Truthiness expect(value).toBeTruthy(); expect(value).toBeFalsy(); expect(value).toBeNull(); expect(value).toBeUndefined(); // Numbers expect(value).toBeGreaterThan(3); expect(value).toBeCloseTo(0.3, 5); // Floating point // Strings/Arrays expect(str).toMatch(/pattern/); expect(arr).toContain(item); expect(arr).toHaveLength(3); // Objects expect(obj).toHaveProperty("key"); expect(obj).toMatchSnapshot(); // Errors expect(() => fn()).toThrow(); expect(() => fn()).toThrow("message"); // Async await expect(promise).resolves.toBe(value); await expect(promise).rejects.toThrow(); ``` ### Hooks ```typescript import { beforeAll, beforeEach, afterEach, afterAll, vi } from "vitest"; beforeAll(async () => { await setupDatabase(); }); beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { // Cleanup }); afterAll(async () => { await teardownDatabase(); }); ``` ### Mocking ```typescript import { vi } from "vitest"; // Mock function const mockFn = vi.fn(); mockFn.mockReturnValue(42); mockFn.mockResolvedValue(data); mockFn.mockImplementation((x) => x * 2); // Assertions expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledWith(arg1, arg2); expect(mockFn).toHaveBeenCalledTimes(3); // Mock module vi.mock("@coop/shared", () => ({ useAuth: vi.fn(() => ({ user: mockUser })), })); // Spy on existing method const spy = vi.spyOn(object, "method"); // Fake timers vi.useFakeTimers(); vi.advanceTimersByTime(1000); vi.useRealTimers(); ``` ### Coop Patterns > For mock factories, hook testing, cleanup patterns, mutation error testing, and offline scenarios, see [vitest-patterns.md](./vitest-patterns.md) **Key patterns summary:** - **Hook testing**: Use `renderHook` + wrapper providers. Always `waitFor` async results. - **Component testing**: Use `render` + `screen` queries. Prefer role-based selectors. - **Mock factories**: Use `createMockCoop`, `createMockTab` etc. from `test-utils/mock-factories.ts`. - **Cleanup testing (Rules 1-3)**: Verify timer cleanup, event listener removal, and isMounted guards on unmount. - **Offline scenarios**: Use `fake-indexeddb/auto` + `vi.spyOn(navigator, "onLine", "get")` to simulate offline. - **Yjs sync testing**: Test CRDT merge behavior with separate Y.Doc instances. **Critical paths** requiring 80%+ coverage (100% for auth/crypto): authentication (passkey), coop creation/join, tab collection, Yjs sync, Dexie persistence, Safe operations, publish flow. ### Commands ```bash bun run test # Run all tests bun run test --watch # Watch mode bun run test -- coop.test.ts # Run specific file bun run test --coverage # With coverage bun run test --ui # UI mode bun run test -t "should validate" # Filter by name ``` ### Coverage Requirements | Package | Critical Paths | Overall | |---------|----------------|---------| | **App** | 80%+ | 70%+ | | **Extension** | 70%+ | 70%+ | | **Shared** | 80%+ (auth/crypto: 100%) | 70%+ | **Measuring coverage:** ```bash bun run test --coverage # Run with coverage open coverage/index.html # View HTML report ``` --- ## Part 3: E2E Testing with Playwright Keep E2E focused on critical user journeys. Use page objects, role-based selectors, and explicit waits. See `.claude/skills/testing/references/playwright.md` for the full Playwright guide (pyramid, selectors, network mocking, a11y, debugging). ### Browser, Chrome, And Computer Use Lanes Choose the proof surface before choosing the command: | Lane | Use When | Commands / Evidence | |------|----------|---------------------| | Browser-first local app | Receiver PWA, app shell, docs, and public localhost routes that do not require sign-in. | `bun run test:e2e:receiver-pwa-eval`, `bun run validate e2e:receiver-pwa-eval`, Browser screenshot or Playwright trace. | | Extension GUI | Popup, sidepanel, extension-card errors, toolbar grants, screenshot capture, and WXT-launched Chromium behavior. | `bun run test:e2e:popup`, `bun run test:e2e:extension`, Computer Use or headed Chromium screenshots when real extension chrome matters. | | Visual regression | Popup/sidepanel layout and screenshot-baseline checks. | `bun run test:visual`; update baselines only when the visual change is intentional and reviewed. | | Signed-in/profiled browser | Flows that depend on Chrome profile state, cookies, or authenticated pages. | Codex Chrome extension evidence with browser/profile recorded. | | Installed PWA / OS prompts | Installed app shell, real media/file-picker prompts, browser permission prompts, and cross-app flows. | Computer Use evidence with target app, route, screenshot, and proof limits. | Do not claim GUI success from unit, typecheck, lint, or headless-only proof when the changed surface depends on real browser chrome, extension permissions, or OS prompts. --- ## Verification Checklist Before marking work complete: ### TDD Requirements - [ ] Every new function/method has a test - [ ] Watched each test fail before implementing - [ ] Wrote minimal code to pass each test - [ ] All tests pass - [ ] Tests use real code (mocks only if unavoidable) - [ ] Edge cases and errors covered ### Validation Commands - [ ] Run `bun format && bun lint && bun run test` -- no errors/warnings - [ ] Package-specific: `cd packages/[pkg] && npx tsc --noEmit` - [ ] UI-impacting changes include Browser, Playwright, Chrome, or Computer Use proof for the affected surface ### Documentation & Communication - [ ] Update relevant documentation when behavior changes - [ ] Surface remaining risks, manual steps, or test gaps in handoff ### Coverage & Pass Thresholds - [ ] Coverage meets package target (70-100% depending on criticality) Can't check all boxes? You skipped TDD. Start over. ## Anti-Patterns - Writing implementation before a failing test is in place - Keeping placeholder assertions (`expect(true).toBe(true)`) - Ignoring cleanup tests for timers/listeners/async hooks - Using brittle snapshots where behavioral assertions are required - Declaring completion without running `bun run test`, `bun lint`, and build checks - Using Computer Use for local app routes when Browser or a Playwright mirror would give cleaner, repeatable proof ## Related Skills - `react` -- React Testing Library patterns and component testing - `data-layer` -- Testing offline scenarios, Dexie with fake-indexeddb, Yjs sync - `browser-verification` -- Rendered browser, Chrome, and Computer Use proof selection - `xstate` -- Testing state machines with Vitest
GitHubで見る