Mock API data generator and JSON fixtures
Give frontend and integration work realistic typed responses before a production backend or stable test environment exists.
Model the response contract
Name fields after the API contract and select generators with matching JSON types. Use Integer or Decimal rather than numeric-looking text, Boolean for flags, Date or Datetime for timestamps and Blank % for nullable properties. Patterns and custom lists cover status codes, SKU shapes and finite enums.
Build useful response states
A happy-path list is only the beginning. Save separate schemas for active users, incomplete profiles, cancelled orders and sparse search results. Keep a fixed seed for visual regression baselines, then change the seed when exploratory testing needs fresh combinations.
JSON array or NDJSON stream?
JSON returns one array and is the natural fit for REST fixtures, browser imports and intercepted responses. NDJSON writes one object per line and is better for log ingestion, bulk endpoints and streaming consumers.
Serve the fixture locally
// Express example
import users from './fixtures/users.json' with { type: 'json' };
app.get('/api/users', (req, res) => res.json(users));
You can also return the file from Playwright route interception, a service worker, json-server or the sibling Fun API Playground.
Wrapping the array in your envelope
Few APIs return a bare array. The export is flat on purpose — reshaping is one jq step, and doing it in your fixture pipeline keeps the transformation visible rather than hidden in a generator option:
# A paginated envelope
jq '{ data: ., page: 1, per_page: length, total: length }' users.json > users-page-1.json
# JSON:API style
jq '{ data: [ .[] | { type: "users", id: (.id|tostring), attributes: . } ] }' users.json
# Key by id for a mock store or MSW handler
jq 'INDEX(.id)' users.json > users-by-id.json
The response states worth generating
Most mock fixtures cover the case the UI was designed around and nothing else, which is why empty states and long values are where the bugs are found later. Each of these is a separate seed against the same schema:
- Empty collection. Set the row count to 1, export, and replace the contents with
[]. Skeleton loaders, pagination controls and "no results" copy all fail differently here. - Exactly one item. Catches pluralisation, grid layouts that assume a second column, and carousels that need two slides.
- A full page plus one. If the page size is 20, generate 21. This is the smallest fixture that exercises pagination honestly.
- Sparse objects. Push Blank % to 40–60% on optional fields. Every unguarded property access surfaces at once.
- Long and international values. Realistic names in several scripts, plus a long free-text field, find truncation and layout breakage that
foonever will. - A large list. 10,000 rows tells you whether the table virtualises or the browser stalls.
Keep them in one directory, one file per state, and the fixture set doubles as documentation of what the component is expected to survive.
Contract-test checklist
- Property names and JSON types match the documented contract.
- Nullable properties include both values and
null. - Dates use the expected timezone and representation.
- IDs are unique where clients use them as keys.
- Strings include realistic long and international values.
- Fixtures contain empty-list and partial-object variants.
Keep fixtures safe
Use generated documentation-domain emails and test-only network/payment values instead of copying real user data. Read the methodology and safety notes, then store the exported schema with the consuming test so future changes remain intentional.
Common questions
How do I create mock JSON data for an API?
Name the fields the way the response names them, using dots for nesting — user.name, user.email — then export JSON. The result is an array of records with real types: numbers stay numbers, booleans stay booleans, and Blank % produces the nulls your client code has to survive.
How do I mock a response envelope around the array?
Generate the array, then paste it into the envelope your API actually returns. The generator produces the collection; the wrapper — a data key, a pagination block, a status field — is a constant, and writing it once by hand is more honest than pretending it was generated.
Should I use JSON or NDJSON for API fixtures?
JSON for a response body, because that is what a client parses. NDJSON when something consumes records one at a time — a stream, a log pipeline, a bulk import endpoint — since each line is a complete record and nothing has to hold the whole array in memory.
How do I test error and empty states with generated data?
Generate them as separate fixtures rather than trying to express them in one. An empty array, a single record, a page-sized batch, and a batch with nulls in every optional field cover most of what a client gets wrong, and each is a one-line change to the row count or a Blank % setting.
Can I type my API client from the same data?
Yes. Paste the generated JSON into the TypeScript, Zod, Pydantic or JSON Schema generator and the declaration is inferred from every record rather than the first, so a key missing from some records comes out optional rather than required.
Last updated