---
id: api/commands/and
title: cy.and()
description: >-
  Create an assertion in Cypress, chained off a previous command and retried
  until it passes or times out.
section: api
source_path: docs/api/commands/and.mdx
version: f60251bf707b0077a9535579c9f90d615440cc35
updated_at: '2026-10-16T10:56:58.670Z'
---
# and

Create an assertion. Assertions are automatically retried as part of the previous command until they pass or time out.

An alias of [`.should()`](/llm/markdown/api/commands/should.md)

**Note:** `.and()` assumes you are already familiar with core concepts such as [assertions](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Assertions)

## Syntax

```
.and(chainers)
.and(chainers, value)
.and(chainers, method, value)
.and(callbackFn)
```

### Usage

**Correct Usage**

```
cy.get('.err').should('be.empty').and('be.hidden') // Assert '.err' is empty & hidden

cy.contains('Login').and('be.visible') // Assert el is visible

cy.wrap({ foo: 'bar' })
  .should('have.property', 'foo') // Assert 'foo' property exists
  .and('eq', 'bar') // Assert 'foo' property is 'bar'
```

**Incorrect Usage**

```
cy.and('eq', '42') // Can not be chained off 'cy'

cy.get('p').and(($p) => {
  cy.log($p) // Can not invoke a Cypress command inside the callback
})
```

### Arguments

**chainers _(String)_**

Any valid chainer that comes from [Chai](/llm/markdown/app/references/assertions.md#Chai) or [Chai-jQuery](/llm/markdown/app/references/assertions.md#Chai-jQuery) or [Sinon-Chai](/llm/markdown/app/references/assertions.md#Sinon-Chai).

**value _(String)_**

Value to assert against chainer.

**method _(String)_**

A method to be called on the chainer.

**callbackFn _(Function)_**

Pass a function that can have any number of explicit assertions within it. Whatever was passed to the function is what is yielded.

### Yields

*   In most cases, `.and()` yields the same subject it was given.
*   `.and()` is an assertion, and it is _safe_ to chain further commands that use the subject. Queries chained after it do retry from the subject it locks in rather than from the top of the chain, which matters when the DOM re-renders. See [Retry boundaries](#Retry-boundaries).

```
cy.get('nav') // yields <nav>
  .should('be.visible') // yields <nav>
  .and('have.class', 'open') // yields <nav>
```

However, some chainers change the subject. In the example below, `.and()` yields the string `sans-serif` because the chainer `have.css, 'font-family'` changes the subject.

```
cy.get('nav') // yields <nav>
  .should('be.visible') // yields <nav>
  .and('have.css', 'font-family') // yields 'sans-serif'
  .and('match', /serif/) // yields 'sans-serif'
```

## Examples

### Chainers

#### Chain assertions on the same subject

```
cy.get('button').should('have.class', 'active').and('not.be.disabled')
```

### Value

#### Chain assertions when yield changes

```
{/* App Code */}
<ul>
  <li>
    <a href="users/123/edit">Edit User</a>
  </li>
</ul>
```

```
cy.get('a')
  .should('contain', 'Edit User') // yields <a>
  .and('have.attr', 'href') // yields string value of href
  .and('match', /users/) // yields string value of href
  .and('not.include', '#') // yields string value of href
```

### Method and Value

#### Assert the href is equal to '/users'

```
cy.get('#header a')
  .should('have.class', 'active')
  .and('have.attr', 'href', '/users')
```

### Function

Passing a function to `.and()` enables you to assert on the yielded subject. This gives you the opportunity to _massage_ what you'd like to assert.

Be sure _not_ to include any code that has side effects in your callback function. Cypress retries the callback until no assertions within it throw.

You cannot invoke Cypress commands inside of an `.and()` callback function. Use Cypress commands before or after `.and()` instead.

**Incorrect Usage**

```
cy.get('p')
  .should('not.be.empty')
  .and(($p) => {
    cy.log($p)
    // ...
  })
```

**Correct Usage**

```
// run the command after the assertion
cy.get('p')
  .should('not.be.empty')
  .and(($p) => {
    // ...
  })
  .then(($p) => {
    cy.log($p)
  })

// or use .then() instead, which does not retry
cy.get('p').then(($p) => {
  // ...
  cy.log($p)
})
```

#### Verify length, content, and classes from multiple `<p>`

```
<div>
  <p class="text-primary">Hello World</p>
  <p class="text-danger">You have an error</p>
  <p class="text-default">Try again later</p>
</div>
```

```
cy.get('p')
  .should('not.be.empty')
  .and(($p) => {
    // should have found 3 elements
    expect($p).to.have.length(3)

    // make sure the first contains some text content
    expect($p.first()).to.contain('Hello World')

    // use jquery's map to grab all of their classes
    // jquery's map returns a new jquery object
    const classes = $p.map((i, el) => {
      return Cypress.$(el).attr('class')
    })

    // call classes.get() to make this a plain array
    expect(classes.get()).to.deep.eq([
      'text-primary',
      'text-danger',
      'text-default',
    ])
  })
```

Using a callback function [will not change the subject](#Subjects)

## Notes

### Chai

#### Similarities to Chai

If you've worked in [Chai](http://chaijs.com/) before, you will recognize that `.and()` matches the same fluent assertion syntax.

Take this _explicit_ assertion for example:

```
expect({ foo: 'bar' }).to.have.property('foo').and.eq('bar')
```

`.and()` reproduces this same assertion behavior.

### Subjects

#### How do I know which assertions change the subject and which keep it the same?

The chainers that come from [Chai](/llm/markdown/app/references/bundled-libraries.md#Chai) or [Chai-jQuery](/llm/markdown/app/references/bundled-libraries.md#Chai-jQuery) will always document what they return.

#### Using a callback function will not change what is yielded

Whenever you use a callback function, its return value is always ignored. Cypress always forces the command to yield the value from the previous cy command's yield (which in the example below is `<button>`)

```
cy.get('button')
  .should('be.visible')
  .and(($button) => {
    expect({ foo: 'bar' }).to.deep.eq({ foo: 'bar' })

    return { foo: 'bar' } // return is ignored, .and() yields <button>
  })
  .then(($button) => {
    // do anything we want with <button>
  })
```

### Retry boundaries

#### An assertion in the middle of a chain locks in the subject

`.should()` and `.and()` retry as part of the command that precedes them. Once every assertion in the chain passes, Cypress locks in the subject at that point, so queries that come after it retry from that subject rather than from the top of the chain. If your application re-renders the DOM in that window, those later queries run against a detached element.

```
cy.get('.list')
  .find('li')
  .eq(2)
  .should('contain', 'Header')
  .and('be.visible') // once these pass, the li element is locked in
  .children('.child') // if the DOM re-renders, this li may be detached
```

Split the chain into separate statements so each one re-queries from the top. [Assertions as retry boundaries](/llm/markdown/app/core-concepts/retry-ability.md#Assertions-as-retry-boundaries) covers the pattern in full.

#### An action command locks in the element it acted on

An action command such as [`.click()`](/llm/markdown/api/commands/click.md) locks in its subject the same way, so an assertion chained after it re-checks that element without re-querying for it. This is why asserting on a class the click toggles works, while asserting on an element the click replaces fails with `The subject is no longer attached to the DOM`.

```
// the click toggles classes on the same element
cy.get('button').click().should('have.class', 'active')

// if the click re-renders the button, alias it and query again
cy.get('button').as('btn').click()
cy.get('@btn').should('have.class', 'active')
```

### Differences

### What's the difference between `.then()` and `.should()`/`.and()`?

`.then()` hands the yielded subject to a callback function and should be used when you need to manipulate some values or do some actions.

When using a callback function with `.should()` or `.and()`, on the other hand, there is special logic to rerun the callback function until no assertions throw within it. You should be careful of side effects in a `.should()` or `.and()` callback function that you would not want performed multiple times.

## Rules

### Requirements

*   `.and()` requires being chained off a previous command.

### Timeouts

*   `.and()` [retries](/llm/markdown/app/core-concepts/retry-ability.md) its specified assertions until they pass or it times out.

```
// timeout here will be passed down to the '.and()'
// and it will retry for up to 10 secs
cy.get('input', { timeout: 10000 })
  .should('have.value', '10')
  .and('have.class', 'error')
```

```
// timeout here will be passed down to the '.and()'
// unless an assertion throws earlier,
// ALL of the assertions will retry for up to 10 secs
cy.get('input', { timeout: 10000 })
  .should('have.value', 'US')
  .and(($input) => {
    expect($input).to.not.be.disabled
    expect($input).to.not.have.class('error')
  })
```

## Command Log

**Chain assertions on the same subject**

```
cy.get('.list')
  .find('input[type="checkbox"]')
  .should('be.checked')
  .and('not.be.disabled')
```

The commands above will display in the Command Log as:

When clicking on `assert` within the command log, the console outputs the following:

## History

| Version | Changes |
| --- | --- |
| [11.0.0](/llm/markdown/app/references/changelog.md#11-0-0) | Throw error if Cypress command used in callback |
| [0.11.4](/llm/markdown/app/references/changelog.md#0-11-4) | Allows callback function argument |
| [0.5.4](/llm/markdown/app/references/changelog.md#0-5-4) | `.and()` repurposed as an alias of `.should()` |

## See also

*   [`.should()`](/llm/markdown/api/commands/should.md)
*   [Guide: Introduction to Cypress](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Assertions)
*   [Reference: List of Assertions](/llm/markdown/app/references/assertions.md)
*   [cypress-example-kitchensink Assertions](https://example.cypress.io/commands/assertions)
