Testing workflow
ReviewedBuild deterministic test fixtures you can replay
A practical workflow for repeatable API fixtures: pin the seed, every shape parameter and any date window or as-of anchor, then save the resolved contract.
- The task
- Create a fixture that stays useful across local runs and CI without freezing accidental defaults into the test.
- Review state
Direct answer
Direct answer
For a replayable fixture, keep the seed and every parameter that changes record shape, and make time explicit. A seed controls the pseudo-random choices; it does not turn an omitted clock-derived default into a permanent date. Prefer an endpoint with an explicit asOf, from/to window or datasetKey, then assert business invariants rather than an entire incidental payload.
Decision support
Choose the route by the job
| When you need | Choose | Why |
|---|---|---|
| Several related database tables | /relational-seed | It returns one internally consistent bundle and can pin the causal clock with asOf. |
| Random instants in a known test window | /dates | Explicit from/to plus seed makes the sampled instants replayable; weekdaysOnly is an exact UTC weekday constraint. |
| Payment-list states and currency edge cases | /transactions | Type, status, amount, currency and date filters constrain every returned record when the range is explicit. |
| Timed and all-day scheduling UI fixtures | /calendar-events | It separates absolute timed instants from exclusive-end all-day date shapes and accepts fixed bounds. |
| Paging, infinite-scroll and empty-boundary tests | /pagination | datasetKey, ordering and total define a clock-free fixture; cursors bind those exact settings. |
What does a seed actually control?
A seed controls the generator's repeatable choices for the same resolved parameters. Parameters remain part of the fixture identity, including defaults that the server resolves from the current date.
Treat the fixture key as a tuple: endpoint, route, seed and resolved parameters. If count, locale, status, currency, schema or a range changes, a different response is expected. This is useful: tests can vary one dimension deliberately without pretending that seed alone owns the whole contract.
For generated list routes, randomapi.dev derives each record from its own position. That gives prefix stability: increasing count keeps the earlier prefix stable for routes covered by the generated-list contract. Relational Seed is stricter about one growth axis: raising customers keeps the existing parent bundles stable, while ordersPerCustomer or itemsPerOrder reshapes child tables and is therefore intentionally not append-only.
How do you remove clock drift from a fixture?
Pass the date boundary yourself. Use asOf where it exists, or supply both from and to; then keep those values with the seed in the test case.
An omitted time window can still be deterministic for the resolved day while moving tomorrow. Dates defaults to the 365 days ending at the current UTC midnight. Transactions defaults to a recent window. Calendar Events derives a UTC-day-snapped future window when neither bound is supplied. Those defaults are convenient for demos, but they are the wrong foundation for a long-lived golden fixture.
The safe pattern is explicit input, not a copied output with no explanation. Pin asOf for a relational bundle, pin from/to for dates, transactions or events, and keep the response's meta.params near the test failure. That makes a changed answer diagnosable: either the inputs changed, the contract changed, or a regression occurred.
- 01
Choose the smallest domain-specific endpoint
A payment test should ask Transactions for failed payments; a database-relation test should use Relational Seed rather than joining unrelated flat generators.
- 02
Pin every shape and time input
Record seed, count, filters, locale and explicit asOf or from/to values. Avoid relying on a current-date default in a snapshot test.
- 03
Assert invariants before snapshots
Check foreign keys, bounds, status/date masks and exact totals. Snapshot only the small fields whose literal stability is the behavior under test.
- 04
Keep a replay URL in the failure output
A complete request is a compact reproduction case that can be opened without rebuilding the original UI or test harness.
What should the test assert?
Assert the promise the endpoint makes: bounds, relationships, ordering and masks. Whole-response snapshots are strongest only when every byte is intentionally contractual.
For Relational Seed, verify that each non-null foreign key points to a parent in the same response and that money totals reconcile in integer minor units. For Dates, verify that every instant sits inside the explicit range and that weekday-only records are Monday through Friday in UTC. For Transactions, check the currency, type, status and amount/date bounds you requested. For Pagination, walk pages and ensure there are no gaps or duplicates.
The response envelope helps with failure messages. meta.params is the server's statement of the values it actually honored, including resolved defaults. Save or print that object when a test fails; it is usually more useful than a large undifferentiated JSON diff.
- The request contains no omitted clock-derived input that the test assumes is fixed.
- The seed and every shape parameter are visible in the fixture setup.
- Assertions cover domain invariants, not only record count or JSON shape.
- The test can print one complete replay URL and meta.params on failure.
- The fixture contains only synthetic data and reserved/example destinations where applicable.
Working requests
Run the concrete examples
A replayable relational SaaS bundle
Pins the generator, schema size and causal time anchor. Every non-null foreign key resolves inside this response, and no generated timestamp is later than asOf.
curl -s 'https://randomapi.dev/api/relational-seed?schema=saas&customers=6&ordersPerCustomer=3&itemsPerOrder=2&seed=42&asOf=2026-01-15T09%3A00%3A00Z' Inspect the response
- meta.params records seed 42 and the resolved 2026-01-15T09:00:00.000Z anchor.
- tableOrder is plans → accounts → users → invoices → invoice_items.
- All five declared foreign keys reference a table earlier in tableOrder.
- Every actual non-null foreign-key value resolves to a parent row returned in the same bundle.
- Every non-null timestamp field in the returned tables is at or before the 2026-01-15T09:00:00Z asOf anchor.
Stable pagination boundary data
Creates a deterministic final partial page without a random seed or hidden state. datasetKey names the stable fixture namespace.
curl -s 'https://randomapi.dev/api/pagination/offset?total=53&page=6&pageSize=10&sort=rank&order=desc&datasetKey=leaderboard' Inspect the response
- The last page contains exactly three items.
- pageInfo is page 6 of 6, covering items 51–53 with no next page.
- meta.params pins total 53, rank/descending order, datasetKey leaderboard, page 6 and pageSize 10.
Material limits
Caveats that change the decision
- Seeded does not mean cryptographically random. Reproducible UUID-shaped or token-shaped fixture values must not be used as secrets or production identifiers.
- Date-only bounds mean UTC midnight on the date. If a whole final day must be eligible, supply its end-of-day timestamp or use a date-shaped endpoint whose end boundary is explicitly exclusive.
- A fixed seed cannot rescue a moving default. Pin asOf or both range boundaries for a durable fixture.
- Relational Seed is internally consistent but intentionally does not match /api/users, /api/products or /api/orders for the same seed.
Endpoint reference
APIs used in this guide
Relational Seed Data API
/api/relational-seed A multi-table seed dataset where every foreign key resolves — JSON tables plus topologically ordered SQL that applies with FK checks left on.
Open endpoint referenceRandom dates
/api/dates Uniformly random timestamps from any date range — ISO instant, Unix seconds, date, time and weekday — with an exact weekdays-only mode and seeded results.
Open endpoint referenceTransactions
/api/transactions Fake account transactions with realistic merchants, weighted statuses and currency-correct amounts — filter by type, status, amount range and date window.
Open endpoint referenceCalendar event fixtures
/api/calendar-events Generate coherent fake calendar events with bounded durations, attendees, IANA timezones and correct timed or all-day date shapes.
Open endpoint referencePagination fixtures
/api/pagination Deterministic offset and opaque-cursor pagination fixtures with exact totals, stable sorting and no duplicated or skipped records.
Open endpoint referenceVisible provenance
Sources used by this guide
- Relational Seed endpoint contract The shipped schema, append-only boundary, SQL dialect and asOf guarantees used in this workflow.
- RFC 9562 §5.4 — UUID version 4 Defines the UUIDv4 layout; the fixture endpoint explicitly does not claim cryptographic randomness.
- SQLite foreign-key support Primary SQLite documentation for the enforcement behavior used by the generated SQLite seed script.