Skilllibrary testing-web
install
source · Clone the upstream repo
git clone https://github.com/merceralex397-collab/skilllibrary
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/08-web-frontend-and-design/testing-web" ~/.claude/skills/merceralex397-collab-skilllibrary-testing-web && rm -rf "$T"
manifest:
08-web-frontend-and-design/testing-web/SKILL.mdsource content
Purpose
Test React applications using Testing Library for component tests, MSW for API mocking, and Vitest as the test runner.
When to use this skill
- writing component tests with
@testing-library/react - mocking API responses with MSW (Mock Service Worker)
- setting up Vitest for a React/Vite project
- testing hooks, async behavior, and user interactions
Do not use this skill when
- writing E2E browser tests — prefer Playwright skills
- testing backend APIs — different testing patterns
- the task is accessibility auditing — prefer
accessibility-audit
Procedure
- Set up Vitest —
.npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom - Configure — in
:vitest.config.ts
,environment: 'jsdom'
.setupFiles: ['./src/test/setup.ts'] - Write component test — render with
, query withrender(<Component />)
, assert withscreen.getByRole()
.expect().toBeInTheDocument() - Simulate interactions — use
,userEvent.click()
. PreferuserEvent.type()
overuserEvent
.fireEvent - Set up MSW — define handlers with
. Start server inhttp.get('/api/data', () => HttpResponse.json(...))
.beforeAll - Test async — use
for elements that appear after async operations. Useawait screen.findByText()
for assertions.waitFor - Test hooks — use
fromrenderHook()
for custom hooks.@testing-library/react - Run —
(watch mode) ornpx vitest
(CI mode).npx vitest run
Component test example
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { expect, test } from 'vitest'; import { Counter } from './Counter'; test('increments count on click', async () => { const user = userEvent.setup(); render(<Counter />); expect(screen.getByText('Count: 0')).toBeInTheDocument(); await user.click(screen.getByRole('button', { name: /increment/i })); expect(screen.getByText('Count: 1')).toBeInTheDocument(); });
MSW API mocking
import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; const server = setupServer( http.get('/api/users', () => HttpResponse.json([ { id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }, ])), ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); test('displays users from API', async () => { render(<UserList />); expect(await screen.findByText('Alice')).toBeInTheDocument(); expect(screen.getByText('Bob')).toBeInTheDocument(); }); test('handles API error', async () => { server.use(http.get('/api/users', () => HttpResponse.error())); render(<UserList />); expect(await screen.findByText(/error/i)).toBeInTheDocument(); });
Decision rules
- Query by role first (
), then by label, then by text — matches how users interact.getByRole - Never query by test ID unless no semantic alternative exists.
- Use
overuserEvent
— it simulates full interaction (focus, keydown, keyup, click).fireEvent - Mock at the network level (MSW) not at the module level — tests real fetch/axios code.
for async,findBy*
for sync,getBy*
to assert absence.queryBy*
References
- https://testing-library.com/docs/react-testing-library/intro
- https://mswjs.io/docs
- https://vitest.dev/
Related skills
— typed component patterns being testedreact-typescript
— testing form validationforms-validation
— testing state interactionsstate-management