Skip to content
Preptima
mediumConceptDesignMidSenior

How do you get a browser test into the state it needs to start, without clicking through the UI to get there?

Create the data through the API, restore an authenticated session from saved cookies and storage rather than logging in each time, and stub only the third parties you do not own, so each test drives the UI solely for the behaviour it is actually asserting.

5 min readUpdated 2026-07-29Target archetype: Big Tech, Enterprise Captive, Product Startup
Practice answering out loud

What the interviewer is scoring

  • Does the candidate build preconditions through the API instead of replaying UI steps
  • Whether authenticated state is reused across tests rather than re-established through the login form
  • That the answer decides what to stub by ownership rather than by convenience
  • Whether the risk of stubbing your own backend into a shape it never returns is acknowledged
  • Does the candidate keep at least one test that exercises the real path the shortcuts bypass

Answer

Setup through the UI is the largest cost in a browser suite

A test that asserts the refund button is disabled on a settled invoice does not need to log in through the form, navigate to customers, create a customer, create an invoice, pay it, and then find the button. Every one of those steps is a chance to fail for a reason unrelated to the assertion, and their combined runtime dwarfs the two seconds the actual check takes. In a suite of two hundred tests, the same login runs two hundred times.

The costs compound in three directions. Runtime, obviously. Reliability, because a test with thirty interactions has thirty opportunities to hit a timing problem, and the failure will be reported against the refund test regardless of where it happened. And diagnosability, because a red refund test that broke in customer creation tells the person triaging it nothing true.

The principle that resolves it is that a test should drive the UI only for the behaviour it is asserting. Everything before that is arrangement, and arrangement should take the cheapest reliable route available.

Data through the API, always

Create the invoice with an HTTP call, not with clicks. Both Playwright and Cypress can issue requests directly from test code, and Playwright's request context can carry the same storage state as the browser so the call is authenticated as the same user. Ten lines of setup replace forty interactions, and if the setup call fails it fails with a status code and a response body rather than a screenshot of a page that did not load.

Prefer the product's own API over writing rows into the database. The API applies the validation and side effects a real caller triggers, so your precondition is a state the product can genuinely reach, and it does not couple your tests to the schema. Direct database writes are the fallback for states no endpoint can produce — an expired trial, a record only a nightly job creates — and they should live in an obviously named helper so nobody uses one out of habit.

Generate the identity of everything you create so tests never collide, and scope every assertion to the records you made rather than to counts on a page that other tests are also writing to.

Log in once, then restore the session

Authentication is arrangement too, and it is the single most repeated piece of it. The pattern in both tools is the same: perform the login once, capture the resulting session — cookies, local storage, whatever your app uses — and restore it before each test so the browser opens already authenticated.

In Playwright this is storage state: a setup project logs in and writes the state to a file, and other projects load it, with different files for each role you need. Cypress provides a session command that caches and restores the login for a given key, so repeated calls within a run reuse the cached state instead of driving the form again.

Two details decide whether this works in practice. Expiry: a token captured at the start of a long run can expire before the last test uses it, so the cached state needs to be keyed and refreshed rather than assumed valid, and a test failing with an unexplained redirect to the login page is the symptom. And isolation between roles: an administrator's session leaking into a test that is meant to run as a read-only user produces a test that passes for the wrong reason, which is worse than a failure.

The consequence is that no test now covers logging in. That is fine and deliberate, provided you write the one test that does — the real form, real credentials, real redirect, plus the failure cases — and treat it as covering a flow rather than as setup.

Stub by ownership, not by convenience

Network interception is powerful enough to be dangerous, because it can make any test pass. The line worth defending is drawn by who owns the endpoint.

Third parties you do not control are legitimate stubs. A payment provider's hosted page, a mapping service, an analytics beacon, an identity provider you cannot drive reliably: these are slow, rate-limited, sometimes charge per call, and their failures are not defects in your product. Stubbing them makes the suite faster and more stable and loses nothing you were testing.

Your own backend is a different matter. Stubbing your own API in a browser test means the test now asserts that the front end renders a response shape you wrote by hand in the test file. When the real endpoint changes that shape, every stubbed test still passes and production breaks. If you are going to stub your own responses at all, the fixtures must be generated from or validated against the real schema, and there must be tests at another layer that exercise the genuine endpoint.

Where interception genuinely earns its place against your own API is for conditions you cannot produce on demand: a 500 from a specific endpoint, a response that takes fifteen seconds so you can assert the loading state, an empty collection, a malformed payload. These are the error and edge states that a real backend will not give you when you ask, and testing them is how you find out whether the front end degrades or shows a blank screen.

Determinism beyond data and auth

Two more sources of setup noise are worth naming because they cause failures that look like flakiness. Time: any assertion about a relative date, an expiry countdown or a scheduled state change is a test that fails on a boundary you did not choose, so control the clock or generate the data relative to now rather than fixing dates. And animation, where an element is present but still moving; both tools wait for stability, but a looping animation or a lazily loaded image can leave a visual assertion permanently unstable, and disabling animation in the test environment is the honest fix rather than adding a wait.

The bypass you must not let become total

Every shortcut here removes a path from your coverage. Seeding invoices through the API means no test creates an invoice through the form. Restoring sessions means nothing exercises the login. Stubbing the payment provider means nothing proves the real integration works. Individually each trade is correct; together, unmanaged, they produce a suite where every test starts from a state the product cannot actually reach and passes against a backend that does not exist.

The discipline is to keep a small number of deliberately end-to-end journeys that use none of the shortcuts — real login, real creation through the UI, real third-party sandbox where one exists — and to accept that they are slow and to run them less often. Name them as a distinct group so nobody optimises them into the fast path, because the moment they take a shortcut, nothing in the suite is testing the assembled system any more.

Likely follow-ups

  • Your saved session expires halfway through a suite run. How should the setup handle that?
  • Where does the login flow itself get tested once every other test skips it?
  • A stubbed response drifts from what the real endpoint returns. How would you find out before your users do?
  • Which parts of setup would you deliberately keep slow and realistic, and why?

Related questions

playwrightcypresstest-setupsession-statenetwork-interception