Mock HTTP Requests in Tests
Use this skill when a test needs the server to answer a request with a known response, and @warp-drive/holodeck is already wired into the suite. Holodeck records the response on the first run and replays it from disk afterwards, so what you write is a declaration rather than a stub.
Steps
Import the helper for the method you need from
@warp-drive/holodeck/mock.GET,POST,PUT,PATCH,DELETE, andHEADall take the same arguments.Declare the mock before the code under test makes the request, passing the test context as the first argument.
tsawait GET(this, 'users/1', () => ({ data: { id: '1', type: 'user', attributes: { name: 'Chris Thoburn' } }, }));Write the URL relative to the mock host, without a leading slash, and exactly as the browser will send it. The server reads the query string through
URLSearchParams, so percent-encode anything outside the unreserved set.users?filter[name]=Chrishas to be writtenusers?filter%5Bname%5D=Chris.Pass
statusin the options object for anything other than a success.tsawait GET(this, 'users/1', () => ({ errors: [{ status: '404' }] }), { status: 404 });Leave
statusTextout. HTTP/2 carries no reason phrase, so the browser never sees the stored value, andFetchderives an error'sstatusTextfrom the status code.Content-Typedefaults toapplication/vnd.api+json.For a request with a body, build the serialized body once and pass the same string to both the mock and the request.
tsconst reqBody = JSON.stringify({ data: { type: 'user', attributes: { firstName: 'Chris' } } }); await POST(this, 'users', () => ({ data: { id: '1', type: 'user' } }), { body: reqBody }); await store.request({ url, method: 'POST', body: reqBody });Run the suite locally to record the fixture, then commit the
.mock-cachefiles it writes.Prove the test replays what you committed. Record-versus-replay is decided when the test bundle is built, not when it runs, so set
CI=1on the build as well as the run. ACI=1that reuses an already-built bundle silently records instead.shCI=1 pnpm build:tests && CI=1 pnpm test
Matching rules
- Holodeck matches on the test id, the method, the URL string, the request body, and a per-URL request counter. A mismatch in method, URL, or body is a
MOCK_NOT_FOUND400 naming the fixture it looked for. A request counter that is off serves a different recorded response with no error at all. - The body is matched by hashing. An object and the JSON string of that object hash differently, so a
bodyoption that is not the exact request string never matches. - Mocks for the same method and URL are consumed in declaration order. Declare them in the order the requests happen.
Notes
- The response function runs only while recording. Do not put assertions or side effects in it.
- A mock the test never requests fails the test from
afterEach, in record and replay alike. Remove it or make the request. RECORD: trueis a per-request override that records even while the suite replays. Use it locally to refresh one fixture and delete it before committing. See Use RECORD in Holodeck Mocks.- Legacy adapters bypass the request handler. Call
installAdapterFor(this, store)for those.
Related
- Full guide: Writing Mocks
- Related skills: Set Up Holodeck if nothing is wired up yet, and Use RECORD in Holodeck Mocks to refresh one fixture