skip to content

In Cypress, what does `cy.wait('@getForecast')` yield to the next command?

level: juniorimportance: must knowfreq 78%

answer

  1. What comes back once the route resolves
  2. One object per request-response cycle
  3. Two halves: what went out, what returned
  4. Also carries id, routeId and error
  5. Chain .its('response.statusCode') to assert

basics

~20 s

cy.wait on an intercept alias yields one interception object for that request-response cycle: request and response, plus id, routeId, and an error property when the request failed. Chain .its() or .then() to assert on it.

solid answer

~40 s

`cy.wait('@getForecast')` resolves once a request matched by the aliased `cy.intercept()` route has completed, and it yields an **interception object** describing that one cycle. The properties you use are `request` (`method`, `url`, `headers`, `body`) and `response` (`statusCode`, `headers`, `body`), alongside `id` and `routeId`; a request that ended in a network error yields `error` and no `response`. Cypress parses `response.body` into an object only when the response carries a `Content-Type: application/json` header — otherwise it stays a string. From there you assert as you would on any subject: `cy.wait('@getForecast').its('response.statusCode').should('eq', 200)`, or `.then(({ request, response }) => { … })` for anything that needs several properties at once. The millisecond form, `cy.wait(ms)`, is different — it yields back whatever subject the chain already had.

code

javascript · 15 lines
javascript
describe('forecast panel', () => {
  it('renders what the forecast API returned', () => {
    cy.intercept('GET', '/api/forecast').as('getForecast')

    cy.visit('/dashboard')

    cy.wait('@getForecast').then(({ request, response }) => {
      expect(request.method).to.equal('GET')
      expect(response.statusCode).to.equal(200)
      expect(response.body.periods).to.have.length(7)
    })

    cy.get('[data-testid="forecast-day"]').should('have.length', 7)
  })
})

go deeper

for a junior

Be ready to name the two halves of what comes back — request and response — and to show one assertion off each, such as .its('response.statusCode').should('eq', 200).

for a middle

Explain why .as() names a route while cy.wait consumes a request, and why response.body is sometimes a string: Cypress parses it only for a JSON content type.

for a senior

Show that you read the interception when a failure is ambiguous, using status, url and headers to say whether the app asked wrongly or the server answered wrongly.

for a principal

Be ready to say how much of a suite should assert on interception internals at all, and what a team convention for that looks like across many specs.

## Two commands, one contract `cy.intercept()` registers a **route**: it tells Cypress which requests to watch and, optionally, what to reply with. The command yields nothing itself, which is why you chain `.as()` onto it — `.as()` names the *route*. `cy.wait('@getForecast')` then blocks the command queue until a request that route matched has completed, and hands the next command a single **interception object** describing that one request-response cycle. The split matters. `.as()` names a route, not a request; `cy.wait()` consumes one request the route matched. Nothing about the alias is a readable value until a request has actually gone through it. ## What is on the interception object | Property | Present when | What it holds | |---|---|---| | `request` | always | The outgoing request: `method`, `url`, `headers`, `body` | | `response` | the server or the stub answered | `statusCode`, `headers`, `body` | | `error` | the request failed at the network level | The captured error; `response` is absent | | `id` | always | Cypress's identifier for this one request-response cycle | | `routeId` | always | The identifier of the `cy.intercept()` route that matched it | A few details about the payload itself are worth carrying into an interview: - `response.body` is parsed into an object **only** when the response carries a `Content-Type: application/json` header. A plain-text body, a `301` redirect or a `304 Not Modified` gives you `response.body` as a **string**. - `request.url` is the fully-qualified URL the browser actually asked for, query string included — not the pattern you wrote in `cy.intercept()`. - The object is a settled snapshot. By the time `cy.wait()` releases the queue, the cycle it describes is finished, so nothing on it changes afterwards. ## Asserting on what came back The interception is an ordinary JavaScript object, so you assert on it the way you assert on any Cypress subject: 1. `.its('response.statusCode').should('eq', 200)` — read one nested value and assert on it. `.its()` is a query, so it starts a fresh chain. 2. `.then((interception) => { … })` — take the whole object and use Chai's `expect` inside the callback. This is what you want when several properties have to be checked together, or when a value has to be derived from the body. 3. `.should('have.property', 'error')` — assert on the interception itself. Useful for the failure case, where there is no `response` to read at all. Destructuring reads well inside `.then()`, because the two properties you almost always want are the top-level ones: `.then(({ request, response }) => …)`. ## The millisecond form yields something else entirely `cy.wait()` has a second signature that takes a number of milliseconds. Given a number, it yields back **the same subject the chain already had** — there is no interception, because no route was involved. That is precisely why the alias form is the one you can chain `.its('response.statusCode')` onto: only the alias form produces a network-shaped subject. ## Where it goes wrong - **Waiting on an alias that is not a route.** A DOM alias or a spy alias fails with `cy.wait() only accepts aliases for routes. The alias: 'x' did not match a route.` - **Treating the yielded object as the body.** `cy.wait('@getForecast').should('have.length', 7)` does not check the forecast array; it checks the interception object, which has no length. - **Expecting an array from one alias.** A single alias yields a single interception. An array comes from passing `cy.wait()` an array of aliases, or from `cy.get('@getForecast.all')`. - **Reading `response` on a failed request.** When the request errored, `response` is `undefined` and `error` is set, so `.its('response.statusCode')` fails with an unhelpful "cannot read property of undefined" instead of surfacing the network error you actually hit. ## In practice A test for a weather dashboard usually wants two things out of one wait: confirmation that the app asked for the right resource, and a look at what came back. ```javascript cy.intercept('GET', '/api/forecast').as('getForecast') cy.visit('/dashboard') cy.wait('@getForecast').then(({ request, response }) => { expect(request.method).to.equal('GET') expect(response.statusCode).to.equal(200) }) ``` The interception is the half of the picture the rendered page cannot show you: it tells you exactly what crossed the wire, with the status and headers attached, which is why a failure here points at a specific request rather than at a missing element.

  • When does `interception.response.body` come back as a string rather than an object?
    Cypress parses the body only when the response carries a `Content-Type: application/json` header. Anything else — a plain-text payload, a `301` redirect, a `304 Not Modified` — yields `response.body` as a string, so `.its('response.body.periods')` will not resolve. Assert on the string, or parse it yourself inside `.then()`.
  • What does `cy.wait('@getForecast')` yield when the request never received a response?
    The interception still comes back, but with an `error` property set and no `response`. That is why Cypress documents `cy.wait('@alias').should('have.property', 'error')` as the way to assert a route ended in a network failure rather than in a status code.

saying these in an interview costs you the question

  • Says cy.wait yields the response body directly
  • Expects an array of interceptions from one alias
  • Thinks cy.wait('@alias') yields a DOM element
  • Assumes response.body is always a parsed object
  • Waits on an alias never attached to cy.intercept