Connect MSW β
MSW intercepts HTTP at the network boundary. Your HttpClient uses the Fetch API, so no code changes are required in the client: point baseUrl (and paths) at URLs your handlers match, and fetch is satisfied by MSW instead of a real server.
This package does not ship MSW; add it to your app or test project:
npm install msw -Dpnpm add msw -Dyarn add msw -DWhen it needs β
- You want the real
HttpClient/fetchpath in tests (URL building, headers, formatters, interceptors), not a stubbed client. - You want one set of handlers for Node and for the browser (or Cypress / Playwright), with the same URL rules.
- Your project already uses MSW for HTTP mocking and you want generated endpoints to plug into that setup.
Node.js (setupServer) β
Use setupServer from msw/node so Nodeβs globalThis.fetch (and therefore HttpClient) is intercepted in the test process.
- Handlers β match the full URL the client will request (
baseUrl+ path, including query if you use it).
mswEndpointHandler wires an endpoint to MSW in one call; see mswEndpointHandler, mswEndpointResponse, and mswPathPattern if you build handlers by hand.
import {
mswEndpointHandler,
mswEndpointResponse,
} from "mobx-tanstack-query-api/testing";
// `listFruits`, `createFruit`: your generated endpoints (GET/POST come from `params().method`).
export const handlers = [
mswEndpointHandler(listFruits, () => ({
items: ["apple", "banana"],
})),
mswEndpointHandler(createFruit, async ({ request }) => {
const body = (await request.json()) as { name: string };
return mswEndpointResponse(
createFruit,
{ id: 1, name: body.name },
{ status: 201 },
);
}),
];- Server lifecycle β register once per test file or globally via your runnerβs setup file (e.g. Vitest
setupFiles).
import { setupServer } from "msw/node";
import { afterAll, afterEach, beforeAll } from "vitest";
import { handlers } from "./handlers";
const server = setupServer(...handlers);
beforeAll(() =>
server.listen({
onUnhandledRequest: "error", // fail fast if a request has no handler
}),
);
afterEach(() => server.resetHandlers());
afterAll(() => server.close());- Use your real
HttpClient(or generated API module) as in production.
import { HttpClient } from "mobx-tanstack-query-api";
const httpClient = new HttpClient({
baseUrl: "https://api.example.com",
});
const res = await httpClient.request({
path: "/api/v1/fruits",
method: "GET",
format: "json",
});
expect(res.data).toEqual({ items: ["apple", "banana"] });Generated endpoints call the same HttpClient.request path, so endpoint.request(...) and EndpointQuery / EndpointMutation flows work as long as the resolved URL matches a handler.
Tips β
- Default status codes β see
testingDefaults; override per response withinit.statuswhere applicable. - Absolute URLs β MSW matches the URL
fetchreceives. IfbaseUrlishttps://api.example.comand the path is/users/1, the handler pattern should behttps://api.example.com/users/1(or use a path predicate /new URL(request.url)inside the resolver for flexibility). - Unhandled requests β
onUnhandledRequest: "error"catches typos inbaseUrlor paths early; relax to"warn"while migrating. - Per-test overrides β
server.use(http.get(...))aftersetupServeradds or replaces handlers for a single test;resetHandlers()clears them inafterEach.
Browser and E2E β
For browser tests (e.g. Cypress, Playwright, or Vite-powered dev), MSW runs in the Service Worker (setupWorker). The same handler URLs apply; ensure the appβs HttpClient baseUrl matches what the worker intercepts (often your dev server origin or a dedicated API host).
When to use MSW vs package Vitest helpers β
| Approach | Best for |
|---|---|
| MSW | Exercising the full HttpClient stack (headers, buildUrl, formatters, interceptors), sharing handlers between browser and Node, or teams already standardized on MSW. |
| Testing helpers | Fast unit tests without MSW (Vitest-first), typed HttpResponse via mockHttpClientRequest* / mockEndpointRequest*, no global listener. |
You can mix both: MSW for integration-style suites and Vitest helpers for narrow endpoint logic.
