Skip to content
Browse docs

Prepare deterministic states without a production backend

Use the application's own fixture paths to make important conditions repeatable, coherent, and honest about missing data.

On this page

Let the application own its data

Mockspace opens the URLs an application declares and renders what the application returns. It is not a mock server. Repeatable screens therefore begin in the app's existing development boundary: its fixture system, mock service worker, seeded database, or route-based scenarios.

Reusing that boundary keeps the review close to normal application behavior. A separate capture-only data layer can miss client requests, bypass the router, or produce a screen that nobody can reach outside the canvas.

Prepare states gives the complete app-owned fixture workflow.

Build one coherent fixture world

A flow should tell one consistent story. If a trip appears in a sidebar, a search result, and a confirmation, its dates, destination, and price should agree across those screens. Page-by-page fixtures can each look plausible while contradicting one another when the frames are arranged in a row.

Start from the application's data contracts and user-facing branches. Include conditions that change what a person sees or decides: lifecycle values, empty and failed lists, optional fields, material size boundaries, and meaningful combinations of independent statuses.

Do not multiply every possible value. A deterministic state earns its place by exposing distinct behavior or layout.

Give a condition a stable address

A fixture identifier in a route is often the clearest selector because it follows the same path as the page's other requests. For example, one identifier can consistently select a completed search with no results across every endpoint the screen uses.

export const states = [
  { id: "default", url: "/trips/trip-searched/inventory" },
  {
    id: "no-results",
    url: "/trips/trip-searched-none/inventory",
    purpose: "A completed empty search must not look like a failure.",
  },
];

A query parameter also works when the application already reads and propagates it. Avoid a global request shim whose only purpose is to pass a capture flag; it is easy for one request to escape that mechanism.

Use actions for local interface state

Some useful conditions have no distinct URL. A state can load a stable page, perform one or more declared interactions, and assert the expected result.

{
  id: "edit-search-dialog",
  url: "/trips/trip-searched/inventory",
  act: [{ click: 'button:has-text("Edit search")' }],
  expect: "[role=dialog]",
  purpose: "Check the dialog against a full result list.",
}

Prefer selectors based on accessible names. An expect assertion keeps a missed click from silently capturing the unopened screen. Read Screens and states for the supported action verbs and static-frame navigation behavior.

Keep failure visible

The capture path must reach the app without an interactive sign-in round trip. Use a supported development gate or a noninteractive cookie the application already recognizes. Do not hide real OAuth inside a capture step.

Unhandled fixture requests should be visible in application logs. A catch-all response can prevent a crash while the fixture set is being prepared, but each request that affects the screen still needs a contract-correct response. Otherwise a frame can look healthy while showing invented or incomplete data.

Verify rendered evidence

Run mockspace check, start the application with the exact environment in its config, and open mockspace dev. Inspect every state after it finishes loading. Confirm that related screens agree, intentional states differ, and none of them shows an auth wall or error boundary.

Two identical states are a finding. Either the selector did not reach the intended fixture, a fallback erased the difference, or the extra state has no review purpose. Troubleshooting provides checks for each case.