From harness-claude
Sets up Mock Service Worker (MSW) to intercept HTTP requests in tests, mocking APIs, errors, delays, and network conditions without modifying app code.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Intercept HTTP requests in tests using Mock Service Worker handlers at the network level
Provides MSW v2 best practices for API mocking in tests: setup and initialization, handler architecture, test integration, response patterns, GraphQL support, and debugging.
Provides TypeScript testing patterns using Vitest for unit tests, MSW for API mocking, typed mocks for dependency injection, and snapshot testing.
Tests Next.js App Router Server Components, Client Components, Server Actions, and Route Handlers using Jest, Vitest, MSW, and next-router-mock.
Share bugs, ideas, or general feedback.
Intercept HTTP requests in tests using Mock Service Worker handlers at the network level
npm install -D msw
// 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 });
}),
];
// test/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// test/setup.ts
import { server } from './mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
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();
});
import { delay, http, HttpResponse } from 'msw';
server.use(
http.get('/api/users', async () => {
await delay(2000);
return HttpResponse.json([]);
})
);
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 });
}),
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');
});
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.
onUnhandledRequest: 'error': Causes tests to fail if the code makes an HTTP request that no handler matches. This catches missing mocks and unintended API calls.
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:
setupServer() — for Node.js test environments (Vitest, Jest)setupWorker() — for browser environments (development server, Storybook)Same handlers work in both modes.
Trade-offs:
onUnhandledRequest: 'error' catches missing mocks — but requires handlers for every request, including static assets in browser mode