Mock client and factories
@noverax/courier-mock/testing builds a typed client that answers from mocks and records every call, and defineFactory builds the records it answers with:
import { courier, defineApi, defineGroup } from "@noverax/courier/define"
import { defineFactory } from "@noverax/courier-mock"
import { createMockClient } from "@noverax/courier-mock/testing"
type User = { id: number; name: string; email: string }
const { endpoint } = courier()
const users = defineGroup({ prefix: "users", endpoints: { getAll: endpoint.get("").response<User[]>() } })
const api = defineApi({ baseURL: "https://api.example.com", groups: { users } })
const userFactory = defineFactory<User>({ defaults: { id: 1, name: "Alice", email: "alice@test.com" } })
const { client, mock, history } = createMockClient<typeof api>()
mock("users", "getAll").resolves(
userFactory.buildSequence(3, (index, base) => ({ ...base, id: index + 1, name: `User ${index + 1}` })),
)
const all = await client.users.getAll() // three users
history.length // 1createMockClient({ network }) answers as a slow or failing network would: it fails a share of calls and delays the rest, with each route's own override applied, and refuses an upload over its size limit or of a type it does not take, with the status a mock server answers it with. See Network conditions for every field, and how each fault fails a call.
Recording a real API
createRecorder calls a real backend from the API's definition and records each response as a fixture — a failed one too. Its client answers as the source's own client does: the data, or an HTTPError with the status and body.
import { createMockClient, createRecorder, replayFixtures } from "@noverax/courier-mock/testing"
const recorder = createRecorder({ source: api, targetUrl: "https://staging.example.com" })
await recorder.client.users.getAll()
const fixtures = recorder.getFixtures() // or recorder.toHar()
const { client: replayed } = createMockClient<typeof api>({ scenario: replayFixtures(fixtures) })- The calls go to each endpoint's own method and path, at
targetUrlor at the source'sbaseURL. To record through a Courier proxy —createCourierServer, or an app with Courier's handler — passproxy: { url, source }: the proxy route's URL and the name the source has there. modeis"record"by default."passthrough"calls the backend and records nothing;"replay"answers from thefixturesgiven, without the network, and rejects a call it has no recording of;"record-missing"replays whatfixtureshas and records the rest.sanitize(data, part)rewrites what is recorded — each call's variables (partis"variables") and each answer's body ("response") — never what the call returns. A recording made with it keeps no query in its URL, and replay passes a call's variables through the same function before looking for their recording; givereplayFixturesandreplayHarthe sanitizer as their third argument for the same reason.Authorization,Proxy-Authorization,CookieandSet-Cookieare never recorded.filternames the endpoints the recorder may call.
Exports
| Entry Point | Description |
|---|---|
@noverax/courier-mock | defineMocks, defineMockServer, defineFactory, defineScenario, MockHttpError |
@noverax/courier-mock/testing | createMockClient, createMockStore, createRecorder, replayFixtures, readRecordings, verifyRecordings — see Tests from Recorded Traffic |
@noverax/courier-mock/server | createMockServer — a standalone Hono-based mock server; persist: { file } keeps its store across restarts in an SQLite database it creates and migrates itself |
@noverax/courier-mock/contract | Contract testing utilities |
@noverax/courier-mock/managed | runMockWorker — the process Studio starts for each mock it runs |