install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/test-msw-pattern" ~/.claude/skills/intense-visions-harness-engineering-test-msw-pattern && rm -rf "$T"
manifest:
agents/skills/claude-code/test-msw-pattern/SKILL.mdsource content
Test MSW Pattern
Intercept HTTP requests in tests using Mock Service Worker handlers at the network level
When to Use
- Testing components or services that make HTTP requests
- Mocking API responses without modifying application code
- Simulating error responses, slow networks, or specific server behaviors
- Sharing mock API definitions between tests and development server
Instructions
- Install and set up MSW:
npm install -D msw
- Define request handlers:
// test/mocks/handlers.ts import { http, HttpResponse } from 'msw'; export const handlers = [ http.get('/api/users', () => { return HttpResponse.json([ { id: '1', name: 'Alice', email: 'alice@test.com' }, { id: '2', name: 'Bob', email: 'bob@test.com' }, ]); }), http.get('/api/users/:id', ({ params }) => { return HttpResponse.json({ id: params.id, name: 'Alice', email: 'alice@test.com', }); }), http.post('/api/users', async ({ request }) => { const body = await request.json(); return HttpResponse.json({ id: 'new-id', ...body }, { status: 201 }); }), ];
- Set up the server for tests:
// test/mocks/server.ts import { setupServer } from 'msw/node'; import { handlers } from './handlers'; export const server = setupServer(...handlers);
- Wire into test setup:
// test/setup.ts import { server } from './mocks/server'; beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); afterEach(() => server.resetHandlers()); afterAll(() => server.close());
- Override handlers per test for specific scenarios:
import { http, HttpResponse } from 'msw'; import { server } from './mocks/server'; it('shows error state when API fails', async () => { server.use( http.get('/api/users', () => { return HttpResponse.json( { message: 'Internal server error' }, { status: 500 }, ); }), ); render(<UserList />); expect(await screen.findByText('Failed to load users')).toBeInTheDocument(); });
- Simulate network delay:
import { delay, http, HttpResponse } from 'msw'; server.use( http.get('/api/users', async () => { await delay(2000); return HttpResponse.json([]); }) );
- Access request details in handlers:
http.post('/api/users', async ({ request, params, cookies }) => { const body = await request.json(); const authHeader = request.headers.get('Authorization'); if (!authHeader) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }); } return HttpResponse.json({ id: 'new', ...body }, { status: 201 }); }),
- Verify requests were made using handler inspection:
it('sends correct headers', async () => { let capturedHeaders: Headers; server.use( http.get('/api/users', ({ request }) => { capturedHeaders = request.headers; return HttpResponse.json([]); }) ); await fetchUsers(); expect(capturedHeaders!.get('Accept')).toBe('application/json'); });
Details
MSW intercepts requests at the network level, below
fetch and XMLHttpRequest. This means your application code is completely unmodified — no dependency injection, no mock modules, no test-specific code paths.
MSW v2 (current): Uses standard
Request/Response objects from the Fetch API. Handlers use http.get(), http.post(), etc. Response construction uses HttpResponse.json(), HttpResponse.text(), etc.
: Causes tests to fail if the code makes an HTTP request that no handler matches. This catches missing mocks and unintended API calls.onUnhandledRequest: 'error'
Handler precedence: Handlers added with
server.use() (per-test overrides) take priority over handlers passed to setupServer() (defaults). server.resetHandlers() removes per-test overrides, restoring defaults.
Browser vs Node: MSW has two modes:
— for Node.js test environments (Vitest, Jest)setupServer()
— for browser environments (development server, Storybook)setupWorker()
Same handlers work in both modes.
Trade-offs:
- Network-level interception is realistic — but cannot test request configuration (timeouts, retries) that happen at the HTTP client level
- Shared handlers between dev and test reduce duplication — but test handlers should be more deterministic than dev handlers
catches missing mocks — but requires handlers for every request, including static assets in browser modeonUnhandledRequest: 'error'- MSW does not mock WebSocket or Server-Sent Events by default — use separate tools for real-time protocol testing
Source
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.