skip to content

When should a Cypress suite be allowed to set testIsolation: false?

level: principalimportance: should knowfreq 38%

answer

  1. It buys one thing and costs another
  2. The blast radius is at least a block
  3. A documented check uses one modifier
  4. Read-only journeys are the honest case
  5. Sequential steps have a cheaper shape

basics

~20 s

Only for a small, read-only block where page loads dominate and no test mutates state another reads, approved on the condition that every test in it still passes under .only. Never at the root config.

solid answer

~40 s

Setting `testIsolation: false` on a `describe` buys exactly one thing: Cypress skips the `about:blank` navigation and the cookie and web-storage clear for that block, so a page and a `before` hook's state survive across its tests. It does not stop Cypress resetting aliases, intercepts, spies, stubs, clock mocks or the viewport, and it cannot be set on an individual `it`. The cost is order coupling: a failure in the fourth test may have been caused by the first, and running one test alone stops reproducing it. Approve it for a small read-only journey where page load dominates, with the documented guardrail that each test still passes under `.only`, and never at the root config. First try the cheaper levers — programmatic seeding, a cached session, or modelling the flow as one longer test.

go deeper

for a junior

Know that the option exists, that it is set on a describe block, and that turning it off means tests in that block share a page and its cookies.

for a middle

Explain exactly what the switch does and does not stop, and why a block that opts out becomes order-dependent in a way the Command Log cannot explain.

for a senior

Show the review instinct: demand the .only check per test, keep the exception scoped to one small block, and name the cheaper levers you tried before reaching for it.

for a principal

Own it as a standard — who may grant the exception, what evidence a request must carry, how it is recorded so a later reader knows the coupling was deliberate, and what the team does when such a block starts failing only in CI.

## What the switch actually changes `testIsolation: false` on a `describe` block tells Cypress not to alter the browser context before each test in that block. Concretely, it stops three things: the navigation to `about:blank`, and the clearing of cookies, `localStorage` and `sessionStorage` across all domains. The page stays exactly as the previous test left it, and state a `before` hook established survives the whole block instead of only the first test. That is the entire benefit, and it is worth naming precisely, because teams often expect more from it than it gives. ## What it does not change - **Cypress still resets its own per-test state.** Aliases, `cy.clock()` mocks, `cy.intercept()` routes, `cy.spy()` and `cy.stub()` doubles and `cy.viewport()` changes are restored before every test whether isolation is on or off. A route stub does not carry into the next `it()`. - **Storage the runner never clears is unaffected either way.** IndexedDB persisted before you flipped the switch and it persists after. - **A cached session still clears cookies and web storage** when it establishes the browser session; with isolation off it simply does not clear the page as well. - **It is a suite-level key.** You cannot put it on an `it()`, and `Cypress.config()` cannot set it during test execution — so the blast radius is always at least a whole `describe` block. - **It does not remove work from the backend.** Records the suite created are still there. ## The cost you take on The block becomes **order-coupled by design**, and three practical things follow: 1. A failure in the fourth test may have been caused by the first. The Command Log for the failing test shows nothing about the borrow that left the basket in a bad state two tests earlier. 2. Running one test alone stops being a reliable reproduction. The very tool you would reach for to isolate a bug is the tool the block has opted out of. 3. Bisecting is manual. There is no runner feature that tells you which earlier test contaminated the later one. Cypress's own guidance is blunt about it: disabling isolation may improve performance, but state leaks between tests, later tests become dependent on earlier ones, and failures become misleading. ## Guardrails before you approve it Treat it as a reviewed exception with conditions, not a default anyone may reach for: 1. **Each test must still pass under `.only`.** This is the documented check, and it is the one that catches accidental coupling. If a test only passes with its neighbours, the block is not a performance optimisation, it is a single long test wearing four names. 2. **Keep the block small and named for one journey** — a read-only walkthrough of the catalogue, a single borrow-and-return flow — not a whole spec file. 3. **Never set it at the root config.** A project-wide `testIsolation: false` removes the safety net from specs nobody reviewed for it. 4. **Seed once in `before`, and do not mutate shared records mid-block.** The block may share a loaded page; it should not share a mutable server-side record with the rest of the suite. 5. **Write down why.** A reader six months later needs to know whether the coupling was deliberate. ## Cheaper levers to reach for first Most requests for `testIsolation: false` are really requests for a faster suite, and the reset is rarely the largest cost: - Seed catalogue data with `cy.request()` or `cy.task()` in a hook instead of driving the UI to create it. - Restore an expensive authenticated context from a cached session rather than repeating a sign-in. - Re-set a known cookie with `cy.setCookie()` in `beforeEach` instead of clicking through a banner. - Model a genuinely sequential journey as **one longer test** rather than four coupled ones. The honest version of "these steps depend on each other" is a single `it()`, which keeps the failure attributable and does not need the switch at all. ## Where it is genuinely defensible A read-only tour — the catalogue landing page, a static help section, a set of marketing routes — where every test only reads and the page load is the dominant cost, is the case the option exists for. The tell is that nothing in the block writes: no borrow, no return, no form submission. As soon as one test mutates state another test reads, the speed you bought is paid back with interest the first time the suite fails in CI and nobody can reproduce it locally.

  • What does a Cypress suite still reset before each test when testIsolation is disabled?
    Its own per-test state: aliases, `cy.clock()` mocks, `cy.intercept()` routes, `cy.spy()` and `cy.stub()` doubles and `cy.viewport()` changes. Only the browser context — the page, cookies, `localStorage` and `sessionStorage` — is left alone. Storage Cypress never clears, such as IndexedDB, is unaffected by the setting in either direction.
  • A team wants testIsolation: false across the whole project to cut a twenty-minute Cypress run. What do you say?
    No at the root, and ask where the twenty minutes actually goes. A project-wide setting removes the reset from every spec, including ones nobody reviewed for coupling, and turns any future ordering change into a mystery. If profiling shows repeated sign-in or UI-driven seeding dominates, fix those directly; if a specific read-only block is the cost, scope the exception to that block.

saying these in an interview costs you the question

  • Sets testIsolation false at the root config
  • Treats it as a general performance setting
  • Thinks it also preserves stubs and intercepts
  • Verifies only that the whole file still passes
  • Uses it to keep four coupled steps as four tests