Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Cypress’s bundled Chai assertions to check JavaScript objects and API responses against the contract your application depends on. For an API response, call cy.request(), inspect its body, and assert the required status, properties, types, values, and nested structure. Use exact-key or deep-equality assertions only when extra or changed data should genuinely fail the test.

Choose an assertion that matches the contract

Cypress includes Chai assertions, so you can use familiar expect(...) assertions or Cypress chains such as .should(...). The important choice is not syntax alone: decide whether the test protects an exact object shape or only the fields the application consumes. Cypress documents its assertion library and supported styles in its Assertions in Cypress reference.

What you need to guarantee Useful style Trade-off
A property has a particular value expect(value).to.eq(expected) or .should('eq', expected) Focused and readable; does not validate unrelated fields.
An object contains required keys expect(object).to.include.all.keys(...) Allows additional keys, which is useful when the API can add fields without affecting the consumer.
An object has exactly the listed keys expect(object).to.have.all.keys(...) Fails if a key is added or removed; use only when the exact shape is part of the contract.
An object equals a known object, including nested values expect(actual).to.deep.eq(expected) Strict and concise for stable, intentionally fixed data; can be brittle for responses with irrelevant fields.
A value has a type or falls within a set or range .to.be.a('number'), .to.be.oneOf([...]), or .to.be.greaterThan(...) Checks a constraint without requiring one specific value.

Prefer assertions that express what a consumer needs. For example, if a client needs id, total, and currency, asserting those fields can be more resilient than rejecting every harmless server-side addition. Conversely, if an extra property would signal a security or compatibility problem, explicitly assert the exact key set.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Validate an API response with cy.request()

cy.request() makes an HTTP request and yields a response containing its status, body, headers, and duration. Cypress parses the response body as a JavaScript object when the response Content-Type ends in json; otherwise the body is a string. Cypress’s API testing guide and cy.request() reference describe the command and response behavior.

Here is a complete test of a cart response, checking the contract at several levels:

describe('GET /cart', () => {
  it('returns a cart the checkout client can use', () => {
    cy.request('/cart').then((response) => {
      expect(response.status).to.eq(200)

      const cart = response.body
      expect(cart).to.have.all.keys(
        'id', 'items', 'subtotal', 'tax', 'total', 'currency'
      )
      expect(cart.id).to.be.a('string').and.not.be.empty
      expect(cart.items).to.be.an('array')
      expect(cart.currency).to.be.oneOf(['USD', 'EUR', 'GBP'])
      expect(cart.subtotal).to.be.a('number')
      expect(cart.tax).to.be.a('number')
      expect(cart.total).to.be.a('number')

      cart.items.forEach((item) => {
        expect(item).to.include.all.keys('sku', 'quantity', 'unitPrice')
        expect(item.sku).to.be.a('string').and.not.be.empty
        expect(item.quantity).to.be.a('number').and.greaterThan(0)
        expect(item.unitPrice).to.be.a('number')
      })
    })
  })
})

The exact cart fields and allowed currencies here are illustrative: use the contract of the API under test. The example deliberately requires an exact top-level key set but only required keys on each item. Remove exactness if new top-level fields are allowed, or add value constraints only where the application depends on them. An assertion that a value is a number does not, by itself, guarantee that it is finite, non-negative, or in an expected range; express those requirements separately when they matter.

Check a single property

For a straightforward property assertion, the chain can be shorter:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
cy.request('/users/1')
  .its('body.username')
  .should('eq', 'jdoe')

This reads well when a test is about one field. For a complete response contract, access the response in .then() and group the related assertions there.

Compare nested data with deep equality

When the expected object is intentionally exact, use a deep comparison:

cy.request('/users/1')
  .its('body')
  .should('deep.eq', { name: 'Jane' })

Deep equality compares nested values rather than checking whether two object references are identical. Keep expected objects small and stable; a full comparison of a response containing timestamps, generated identifiers, or unrelated fields can make a test noisy and fragile.

Test validation errors intentionally

By default, a non-success response can cause cy.request() to fail the command. When the purpose of the test is to verify an error response, set failOnStatusCode: false so the test can inspect it:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
it('rejects an order without line items', () => {
  cy.request({
    method: 'POST',
    url: '/orders',
    body: { lineItems: [] },
    failOnStatusCode: false,
  }).then((response) => {
    expect(response.status).to.eq(422)
    expect(response.body.errors).to.deep.include({
      field: 'lineItems',
      message: 'must contain at least one item',
    })
  })
})

The 422 status and error shape are examples, not universal rules. Assert the status code and error payload that your service promises. Use a partial deep inclusion when an error array may contain additional entries; use exact equality only if the full error collection is contractually fixed.

Understand retries before choosing should() or then()

Use .should() when Cypress can retry the subject while waiting for a condition to become true, particularly for asynchronously changing UI state. A .should(callback) callback groups assertions, and Cypress retries that assertion block while the subject remains retryable. Keep callbacks free of side effects because Cypress may run them more than once. See the Cypress introduction for retry behavior.

Use .then() for ordinary synchronous assertions on a resolved response. A cy.request() has already completed when its response is yielded: assertions chained from that request run once. Do not expect a failed body assertion to repeat the HTTP request. Request options for retrying eligible network or status failures are separate from Cypress’s assertion retry mechanism; configure them deliberately according to the request API reference rather than treating them as assertion retries.

For example, this is appropriate for a response that has arrived:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
cy.request('/users/1').then((response) => {
  expect(response.status).to.eq(200)
  expect(response.body.active).to.eq(true)
})

A UI value that appears after an asynchronous update is a different case; assert against the retryable Cypress subject instead of capturing a one-time value and expecting Chai to poll it.

Use fixtures for reusable test data

Keep a small, case-specific expected value inline when that makes the test easier to understand beside its assertions. Put substantial or shared test data in a fixture. Cypress’s cy.fixture() API reference covers fixture loading and the supported fixture formats.

it('checks a user fixture', () => {
  cy.fixture('user.json').then((user) => {
    expect(user).to.include.all.keys('id', 'username', 'active')
    expect(user.username).to.be.a('string')
    expect(user.active).to.be.a('boolean')
  })
})

Make the fixture’s format and the assertion agree: a JSON fixture supplies parsed data, while other formats may yield text or another representation. Do not use a fixture merely to move a tiny, one-off expected value away from the test that explains it.

Avoid assertions that pass for the wrong reason

A negative assertion can be technically true while the application is broken in a different way. For example, asserting that a list does not contain a particular item does not establish that the right items remain: the application might have deleted everything or inserted a blank item. Cypress’s assertions reference discusses this weakness with list-count examples. Assert the resulting expected value, shape, or count directly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Instead of only asserting that a bad value is absent, assert that the required valid values are present.
  • Instead of only asserting that a list count is not a particular number, assert the intended count or expected list content.
  • Instead of checking only that a response field exists, check its type and any range or format the consumer requires.
  • Use exact-key assertions only where rejecting additional fields is intentional; otherwise assert required keys.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common data-validation failures

The response body is a string, not an object

Cypress yields a string when the response content type does not end in json. Inspect response.headers and the server’s response headers; correct the API content type or explicitly parse the text if that is the documented response format. Avoid assuming response.body.someProperty is valid for every endpoint.

A request fails before the error body can be checked

For a test that expects a non-success status, add failOnStatusCode: false to the request options, then assert the expected status and response structure. Without that setting, default request behavior treats an unsuccessful response as a command failure.

An exact-key assertion breaks after an API change

Determine whether the extra or changed key violates a consumer-facing contract. If it is a harmless addition, change the test to assert that required keys are included rather than asserting that all keys are exactly equal. Keep exactness when the consumer or compatibility requirements depend on it.

A should() assertion is not waiting for an API response

Assertions on a completed cy.request() response are not automatically retried. A retryable .should() is useful for supported Cypress subjects such as changing UI state; it does not turn a completed HTTP request into a polling loop. If the application requires polling, implement that behavior explicitly rather than relying on an assertion to resend the request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A negative assertion passes despite broken data

Replace it with a direct assertion of the desired result: an expected item, a precise count, or a required object shape. Negative checks are useful only when absence itself is the meaningful contract and the test also rules out incorrect alternate outcomes.

Or skip the browser setup

Screenshot capture is separate from Cypress data assertions: ScreenshotNeo does not replace a test of an API object’s shape or values. If a separate part of your workflow needs a website screenshot, its API can capture a URL without setting up a browser automation script. The ScreenshotNeo website describes its screenshot API and MCP server.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for request details. It removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server provides screenshot tools for AI agents. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up for ScreenshotNeo’s free plan to try screenshot capture with 1,000 shots a month and no card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can Cypress validate a JavaScript object that was not returned by an API?

Yes. Pass the object directly to Chai’s expect() assertions, or assert against a Cypress subject that yields that object; the same property, key, type, and equality checks apply.

Does cy.request() test the application through the browser UI?

No. It makes an HTTP request directly and yields the response. Use a browser-interaction test when the behavior you need to verify depends on the rendered interface or user actions.

Should every API response test assert every field?

No. Assert the fields and constraints that protect the consumer-facing contract; require an exact key set only when additional fields should be considered a failure.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.