skip to content

Canned Inputs

Where a test's data comes from when it is not typed into the spec: files read off disk, and records prepared outside the browser. Interviewers ask because setup cost dominates a suite.

on this pageshow

explore

questions

10

Cypress 16 removed cy.exec() - how do you run a setup command now?

level: juniorimportance: must knowfreq 58%

answer

  1. The shell door closed in version 16
  2. Setup now runs in Cypress's Node process
  3. One command replaced by an event name
  4. cy.task supersedes the removed cy.exec
  5. execTimeout became taskTimeout, 60000 ms

basics

~20 s

cy.exec() was removed in Cypress 16 and now throws; use cy.task() instead, calling a handler registered under the task event in setupNodeEvents. The execTimeout option was removed with it, and taskTimeout, default 60000 ms, replaces it.

solid answer

~50 s

`cy.exec()` was removed in Cypress 16.0.0 after being deprecated in 15.21.0; calling it throws an error pointing you at `cy.task()`. `cy.exec()` shelled out on the machine running Cypress, so its behaviour depended on the operating system, the shell, the login environment and whether a terminal was attached. `cy.task(event, arg, options)` instead invokes a handler registered under the `task` event in `setupNodeEvents`, running in the Node process that loaded your Cypress config - no shell resolution, no per-platform quoting. It also hands back a real value rather than stdout you have to parse, so `cy.task('seedExpenseReport', { total: 42.5 })` can yield the created report object. The handler must return a value or `null`; `undefined` fails the command. `execTimeout` went with the command - setting it only prints a warning - and `taskTimeout` (60000 ms) bounds `cy.task()`.

code

javascript · 15 lines
javascript
// Removed in Cypress 16 - this now throws:
// cy.exec('node scripts/seed-expenses.js').its('stdout')

describe('expense report detail', () => {
  beforeEach(() => {
    cy.task('resetExpenseLedger')
    cy.task('seedExpenseReport', { employee: 'jane', total: 42.5 }).then((report) => {
      cy.visit(`/reports/${report.id}`)
    })
  })

  it('shows the submitted receipt total', () => {
    cy.get('[data-cy="report-total"]').should('have.text', '$42.50')
  })
})

go deeper

for a junior

Recall that cy.exec() is gone in Cypress 16 and cy.task() is the replacement. Be ready to say where a task handler is registered and that a task must return a value or null.

for a middle

Explain why the shell was the problem: OS, shell, login environment and attached terminal all leaked into the test. Contrast parsing stdout with receiving a structured value.

for a senior

Be ready to describe migrating a real suite off cy.exec() - finding the call sites, deciding which become tasks and which become HTTP calls, and deleting execTimeout from the config.

for a principal

Own the argument for keeping the Node-side surface small: every task is a permanent extension point your team maintains, so justify each one rather than porting shell scripts across wholesale.

## What was removed, and what happens if you call it `cy.exec()` is gone as of Cypress **16.0.0**. It was deprecated in `15.21.0` and removed in the next major. Calling it in a spec no longer runs anything: the driver throws immediately with *"`cy.exec()` was removed in Cypress version 16.0.0. Please update to use `cy.task()` instead, which runs in Node and does not depend on the operating system, shell, or terminal of the machine running Cypress."* The `execTimeout` configuration option went with it. Cypress prints a warning saying it was removed in 16.0.0 and then ignores the value, so a configuration that still carries `execTimeout: 120000` bounds nothing at all. Delete it and reach for `taskTimeout` instead. This is worth knowing precisely, because a large share of published Cypress material still opens its seeding section with `cy.exec('npm run db:reset')`. On Cypress 16 that line is a hard failure at the moment the command runs, not a deprecation notice you can defer. ## Why the shell door was closed `cy.exec()` ran a command **through the shell on the machine running Cypress**, which made a test's setup step depend on things a test has no business depending on: - the operating system, and therefore which quoting and escaping rules apply to the command string - which shell is configured, and whether its login profile has been sourced - whether a terminal is attached — different on a developer laptop than under a CI runner - `PATH` resolution for the binary named in the string - the output contract: you received `stdout` as text and had to parse a value back out of it `cy.task()` has none of that surface. Its handler runs in the **Node process that loaded your Cypress configuration**, so there is no shell, no per-platform quoting, no `PATH` guessing — and the value the handler returns arrives in the spec as a value, not as text. ## The replacement, end to end 1. Register a handler for the `task` event inside `setupNodeEvents`, keyed by an event name of your choosing (`'seedExpenseReport'`, `'resetExpenseLedger'`). 2. Call `cy.task(event)`, `cy.task(event, arg)` or `cy.task(event, arg, options)` from the spec. 3. Consume the yielded value with `.then()`, `.its()` or a chained assertion. From the spec's side, the only contract is the event name and whatever comes back; the handler itself lives with the rest of your Cypress configuration. | Cypress 15 and earlier | Cypress 16 | |---|---| | `cy.exec('node seed-expenses.js')` | `cy.task('seedExpenseReport', payload)` | | yields `{ stdout, stderr, exitCode }` | yields whatever the handler returned | | `execTimeout` bounds it | `taskTimeout` bounds it, default `60000` ms | | runs in a shell on the host machine | runs in Cypress's own Node process | | output is text the spec must parse | value arrives already structured | ## The contract `cy.task()` imposes - **The handler must return something.** Returning `undefined` fails the command with *"The task 'X' returned undefined. You must return a value, null, or a promise that resolves to a value or null to indicate that the task was handled."* Return `null` explicitly when there is nothing to hand back. - **An unregistered event name fails too**, and the error lists every task name that *is* registered, which makes a typo obvious rather than mysterious. - **Exactly one argument crosses the boundary.** `cy.task(name, arg, options)` has room for a single `arg`; pass an object and destructure it on the Node side when you need several values. - **The argument is serialized with `JSON.stringify()`.** Functions, regular expressions and symbols are omitted to `null`, and a `Date` arrives on the other side as a string. ## What if the thing you need to run is not Node? Spawn it from inside the handler. Node's `child_process.execFileSync` with an argument array — `execFileSync('docker', ['exec', 'db', 'reset-script'])` — runs the binary directly and skips the shell, which is exactly the property whose absence made `cy.exec()` unreliable. The difference is that the spawning is now explicit, lives in one place instead of scattered through specs, and the task can hand the spec a real value rather than a blob of stdout. ## What this looks like in an expense-report suite A suite that used to shell out to reset the ledger and then scrape a report id out of stdout now calls `cy.task('resetExpenseLedger')` followed by `cy.task('seedExpenseReport', { employee: 'jane', total: 42.5 })`, and the second call yields the created report object. The spec goes straight to `cy.visit('/reports/' + report.id)` with no string parsing, no `.trim()`, and no guessing about which shell CI happened to give it.

  • In Cypress 16, what happens if you leave execTimeout in your configuration file?
    It is ignored. Cypress prints a warning saying `execTimeout` was removed in 16.0.0 and points you at `cy.task()` and `taskTimeout`, then carries on with the option having no effect at all. Delete it, and set `taskTimeout` if the default 60 seconds is not enough for your seeding work.
  • Your setup needs a database CLI that is not a Node module. How, without cy.exec()?
    Spawn it from inside the task handler. Node's `child_process.execFileSync` with an argument array runs the binary directly and avoids the shell, so quoting and `PATH` behave the same on every platform. The handler returns whatever the spec needs - the output, or `null` - and the spec still just calls `cy.task('resetExpenseLedger')`.

saying these in an interview costs you the question

  • Claims cy.exec() still works if you avoid CI
  • Parses stdout from a task instead of returning a structured value
  • Thinks cy.task() runs in the browser beside the test code
  • Keeps execTimeout in the config expecting it to bound tasks
  • Returns nothing from the handler and calls the failure a Cypress bug
open as a page

In Cypress, how does cy.fixture('receipts') resolve a file with no extension?

level: juniorimportance: must knowfreq 76%

basics

~20 s

Cypress joins the path onto fixturesFolder, which defaults to cypress/fixtures. If that exact file exists it is read; otherwise Cypress tries a fixed extension list, .json first, and yields the first match, parsed according to that extension.

open as a page

Why doesn't a Cypress cy.request() appear in the browser's Network tab?

level: middleimportance: must knowfreq 70%

basics

~20 s

Cypress makes the request from its Node process, not as a browser XHR, so DevTools never shows it. Because the browser never issues it, CORS is bypassed and cy.intercept does not see it; cookies are still attached and applied.

open as a page

In a Cypress test, how do you read fixture data aliased with .as('receipts')?

level: middleimportance: should knowfreq 58%

basics

~20 s

Two ways: this.receipts inside a test written with function(), because Cypress stores non-DOM aliases on Mocha's shared test context, or cy.get('@receipts') followed by .then(). Either only works after the queued .as() command has actually run.

open as a page

In Cypress, which files can cy.readFile() reach that cy.fixture() cannot?

level: middleimportance: should knowfreq 46%

basics

~20 s

Anything under the project root. cy.readFile() resolves its path from the folder holding the Cypress config, so it reads a downloaded reimbursement CSV or a generated report, while cy.fixture() is confined to fixturesFolder, by default cypress/fixtures.

open as a page

A Cypress cy.task() seeding a database times out after 60 seconds - what do you do?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Sixty seconds is the taskTimeout default. Diagnose first: a task that never ends, or waits on a dependency, will not be fixed by a bigger number. If the work is genuinely heavy, raise the narrowest scope, ideally the per-call timeout option.

open as a page

In a Cypress run, how many times does cy.fixture() actually read a file from disk?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Once per path and encoding. Cypress caches the parsed contents in the browser-side driver and serves every later call from that cache, handing back a fresh clone each time so one test cannot corrupt another's copy.

open as a page

When should a Cypress suite seed through cy.request() rather than cy.task()?

level: principalimportance: should knowfreq 38%

basics

~20 s

Default to cy.request when an endpoint can express the state, because the record goes through the app's own validation and the session travels with it. Reserve cy.task for states no endpoint exposes and for work that is not HTTP.

open as a page

Why does a Cypress cy.request() to a redirecting URL yield 200 instead of 302?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Because followRedirect defaults to true, so Cypress follows the chain and yields the last response. Set followRedirect: false to stop at the first one; then status is 302 and redirectedToUrl holds the absolute Location target.

open as a page

In Cypress, why is cy.fixture() a poor way to load a 40 MB receipt archive?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

Because it reads the whole file into memory in the Cypress Node process, ships it to the browser as a single internal WebSocket message, and keeps it cached. Large archives can exhaust memory or exceed socket buffer limits.

open as a page