---
id: app/references/assertions
title: 'Assertions in Cypress: A Guide'
description: >-
  Assertions available in Cypress with Chai, Chai-jQuery, and Sinon-Chai
  assertions
section: app
source_path: docs/app/references/assertions.mdx
version: e6988a974973e9090ce70406c38cb2b9e0eac9fa
updated_at: '2026-05-15T15:50:22.536Z'
---
# Assertions

##### What you'll learn

*   Assertions available in Cypress with Chai, Chai-jQuery, and Sinon-Chai assertions
*   How to write assertions for common use cases
*   How to chain assertions together

Cypress bundles the popular [Chai](/llm/markdown/app/references/assertions.md#Chai) assertion library, as well as helpful extensions for [Sinon](/llm/markdown/app/references/assertions.md#Sinon-Chai) and [jQuery](/llm/markdown/app/references/assertions.md#Chai-jQuery), bringing you dozens of powerful assertions for free.

If you're looking to understand **how** to use these assertions please read about assertions in our [Introduction to Cypress](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Assertions) guide.

## Chai

[https://github.com/chaijs/chai](https://github.com/chaijs/chai)

These chainers are available for BDD assertions (`expect`/`should`). Aliases listed can be used interchangeably with their original chainer. You can see the entire list of available BDD Chai assertions [here](http://chaijs.com/api/bdd/).

| Chainer | Example |
| --- | --- |
| not | `.should('not.equal', 'Jane')`  
`expect(name).to.not.equal('Jane')` |
| deep | `.should('deep.equal', { name: 'Jane' })`  
`expect(obj).to.deep.equal({ name: 'Jane' })` |
| nested | `.should('have.nested.property', 'a.b[1]')`  
`.should('nested.include', {'a.b[1]': 'y'})`  
`expect({a: {b: 'x'}}).to.have.nested.property('a.b')`  
`expect({a: {b: 'x'}}).to.nested.include({'a.b': 'x'})` |
| ordered | `.should('have.ordered.members', [1, 2])`  
`expect([1, 2]).to.have.ordered.members([1, 2])`  
`expect([1, 2]).not.to.have.ordered.members([2, 1])` |
| any | `.should('have.any.keys', 'age')`  
`expect(arr).to.have.any.keys('age')` |
| all | `.should('have.all.keys', 'name', 'age')`  
`expect(arr).to.have.all.keys('name', 'age')` |
| a(_type_)  
**Aliases:** an | `.should('be.a', 'string')`  
`expect('test').to.be.a('string')` |
| include(_value_)  
**Aliases:** contain, includes, contains | `.should('include', 2)`  
`expect([1,2,3]).to.include(2)` |
| ok | `.should('not.be.ok')`  
`expect(undefined).to.not.be.ok` |
| true | `.should('be.true')`  
`expect(true).to.be.true` |
| false | `.should('be.false')`  
`expect(false).to.be.false` |
| null | `.should('be.null')`  
`expect(null).to.be.null` |
| undefined | `.should('be.undefined')`  
`expect(undefined).to.be.undefined` |
| exist | `.should('exist')`  
`expect(myVar).to.exist` |
| empty | `.should('be.empty')`  
`expect([]).to.be.empty` |
| arguments  
**Aliases:** Arguments | `.should('be.arguments')`  
`expect(arguments).to.be.arguments` |
| equal(_value_)  
**Aliases:** equals, eq | `.should('equal', 42)`  
`expect(42).to.equal(42)` |
| deep.equal(_value_) | `.should('deep.equal', { name: 'Jane' })`  
`expect({ name: 'Jane' }).to.deep.equal({ name: 'Jane' })` |
| eql(_value_)  
**Aliases:** eqls | `.should('eql', { name: 'Jane' })`  
`expect({ name: 'Jane' }).to.eql({ name: 'Jane' })` |
| greaterThan(_value_)  
**Aliases:** gt, above | `.should('be.greaterThan', 5)`  
`expect(10).to.be.greaterThan(5)` |
| least(_value_)  
**Aliases:** gte | `.should('be.at.least', 10)`  
`expect(10).to.be.at.least(10)` |
| lessThan(_value_)  
**Aliases:** lt, below | `.should('be.lessThan', 10)`  
`expect(5).to.be.lessThan(10)` |
| most(_value_)  
**Aliases:** lte | `.should('have.length.of.at.most', 4)`  
`expect('test').to.have.length.of.at.most(4)` |
| within(_start_, _finish_) | `.should('be.within', 5, 10)`  
`expect(7).to.be.within(5, 10)` |
| instanceOf(_constructor_)  
**Aliases:** instanceof | `.should('be.instanceOf', Array)`  
`expect([1, 2, 3]).to.be.instanceOf(Array)` |
| property(_name_, _\[value\]_) | `.should('have.property', 'name')`  
`expect(obj).to.have.property('name')` |
| deep.property(_name_, _\[value\]_) | `.should('have.deep.property', 'tests[1]', 'e2e')`  
`expect(deepObj).to.have.deep.property('tests[1]', 'e2e')` |
| ownProperty(_name_)  
**Aliases:** haveOwnProperty, own.property | `.should('have.ownProperty', 'length')`  
`expect('test').to.have.ownProperty('length')` |
| ownPropertyDescriptor(_name_)  
**Aliases:** haveOwnPropertyDescriptor | `.should('have.ownPropertyDescriptor', 'a')`  
`expect({a: 1}).to.have.ownPropertyDescriptor('a')` |
| lengthOf(_value_) | `.should('have.lengthOf', 4)`  
`expect('test').to.have.lengthOf(4)` |
| match(_RegExp_)  
**Aliases:** matches | `.should('to.match', /^test/)`  
`expect('testing').to.match(/^test/)` |
| string(_string_) | `.should('have.string', 'test')`  
`expect('testing').to.have.string('test')` |
| keys(_key1_, _\[key2\]_, _\[...\]_)  
**Aliases:** key | `.should('have.keys', 'pass', 'fail')`  
`expect({ pass: 1, fail: 2 }).to.have.keys('pass', 'fail')` |
| throw(_constructor_)  
**Aliases:** throws, Throw | `.should('throw', Error)`  
`expect(fn).to.throw(Error)` |
| respondTo(_method_)  
**Aliases:** respondsTo | `.should('respondTo', 'getName')`  
`expect(obj).to.respondTo('getName')` |
| itself | `.should('itself.respondTo', 'getName')`  
`expect(Foo).itself.to.respondTo('bar')` |
| satisfy(_method_)  
**Aliases:** satisfies | `.should('satisfy', (num) => num > 0)`  
`expect(1).to.satisfy((num) => num > 0)` |
| closeTo(_expected_, _delta_)  
**Aliases:** approximately | `.should('be.closeTo', 1, 0.5)`  
`expect(1.5).to.be.closeTo(1, 0.5)` |
| members(_set_) | `.should('include.members', [3, 2])`  
`expect([1, 2, 3]).to.include.members([3, 2])` |
| oneOf(_values_) | `.should('be.oneOf', [1, 2, 3])`  
`expect(2).to.be.oneOf([1,2,3])` |
| change(_function_)  
**Aliases:** changes | `.should('change', obj, 'val')`  
`expect(fn).to.change(obj, 'val')` |
| increase(_function_)  
**Aliases:** increases | `.should('increase', obj, 'val')`  
`expect(fn).to.increase(obj, 'val')` |
| decrease(_function_)  
**Aliases:** decreases | `.should('decrease', obj, 'val')`  
`expect(fn).to.decrease(obj, 'val')` |

These getters are also available for BDD assertions. They don't actually do anything, but they enable you to write clear, English sentences.

| Chainable getters |
| --- |
| `to`, `be`, `been`, `is`, `that`, `which`, `and`, `has`, `have`, `with`, `at`, `of`, `same` |

## Chai-jQuery

[https://github.com/chaijs/chai-jquery](https://github.com/chaijs/chai-jquery)

These chainers are available when asserting about a DOM object.

You will commonly use these chainers after using DOM commands like: [`cy.get()`](/llm/markdown/api/commands/get.md), [`cy.contains()`](/llm/markdown/api/commands/contains.md), etc.

| Chainers | Assertion |
| --- | --- |
| attr(_name_, _\[value\]_) | `.should('have.attr', 'bar')`  
`expect($el).to.have.attr('foo', 'bar')` |
| prop(_name_, _\[value\]_) | `.should('have.prop', 'disabled', false)`  
`expect($el).to.have.prop('disabled', false)` |
| css(_name_, _\[value\]_) | `.should('have.css', 'background-color', 'rgb(0, 0, 0)')`  
`expect($el).to.have.css('background-color', 'rgb(0, 0, 0)')` |
| data(_name_, _\[value\]_) | `.should('have.data', 'foo', 'bar')`  
`expect($el).to.have.data('foo', 'bar')` |
| class(_className_) | `.should('have.class', 'foo')`  
`expect($el).to.have.class('foo')` |
| id(_id_) | `.should('have.id', 'foo')`  
`expect($el).to.have.id('foo')` |
| html(_html_) | `.should('have.html', 'I love testing')`  
`expect($el).to.have.html('with Cypress')` |
| text(_text_) | `.should('have.text', 'I love testing')`  
`expect($el).to.have.text('with Cypress')` |
| value(_value_) | `.should('have.value', 'test@dev.com')`  
`expect($el).to.have.value('test@dev.com')` |
| visible | `.should('be.visible')`  
`expect($el).to.be.visible` |
| hidden | `.should('be.hidden')`  
`expect($el).to.be.hidden` |
| selected | `.should('be.selected')`  
`expect($option).not.to.be.selected` |
| checked | `.should('be.checked')`  
`expect($input).not.to.be.checked` |
| focus\[ed\] | `.should('have.focus')`  
`expect($input).not.to.be.focused`  
`expect($input).to.have.focus` |
| enabled | `.should('be.enabled')`  
`expect($input).to.be.enabled` |
| disabled | `.should('be.disabled')`  
`expect($input).to.be.disabled` |
| empty | `.should('be.empty')`  
`expect($el).not.to.be.empty` |
| exist | `.should('exist')`  
`expect($nonexistent).not.to.exist` |
| match(_selector_) | `.should('match', ':empty')`  
`expect($emptyEl).to.match(':empty')` |
| contain(_text_) | `.should('contain', 'text')`  
`expect($el).to.contain('text')` |
| descendants(_selector_) | `.should('have.descendants', 'div')`  
`expect($el).to.have.descendants('div')` |

## Sinon-Chai

[https://github.com/domenic/sinon-chai](https://github.com/domenic/sinon-chai)

These chainers are used on assertions with [`cy.stub()`](/llm/markdown/api/commands/stub.md) and [`cy.spy()`](/llm/markdown/api/commands/spy.md).

| Sinon.JS property/method | Assertion |
| --- | --- |
| called | `.should('have.been.called')`  
`expect(spy).to.be.called` |
| callCount | `.should('have.callCount', 3)`  
`expect(spy).to.have.callCount(n)` |
| calledOnce | `.should('have.been.calledOnce')`  
`expect(spy).to.be.calledOnce` |
| calledTwice | `.should('have.been.calledTwice')`  
`expect(spy).to.be.calledTwice` |
| calledThrice | `.should('have.been.calledThrice')`  
`expect(spy).to.be.calledThrice` |
| calledBefore | `.should('have.been.calledBefore', spy2)`  
`expect(spy1).to.be.calledBefore(spy2)` |
| calledAfter | `.should('have.been.calledAfter', spy2)`  
`expect(spy1).to.be.calledAfter(spy2)` |
| calledWithNew | `.should('have.been.calledWithNew')`  
`expect(spy).to.be.calledWithNew` |
| alwaysCalledWithNew | `.should('have.always.been.calledWithNew')`  
`expect(spy).to.always.be.calledWithNew` |
| calledOn | `.should('have.been.calledOn', context)`  
`expect(spy).to.be.calledOn(context)` |
| alwaysCalledOn | `.should('have.always.been.calledOn', context)`  
`expect(spy).to.always.be.calledOn(context)` |
| calledWith | `.should('have.been.calledWith', ...args)`  
`expect(spy).to.be.calledWith(...args)` |
| alwaysCalledWith | `.should('have.always.been.calledWith', ...args)`  
`expect(spy).to.always.be.calledWith(...args)` |
| calledOnceWith | `.should('have.been.calledOnceWith', ...args)`  
`expect(spy).to.be.calledOnceWith(...args)` |
| calledWithExactly | `.should('have.been.calledWithExactly', ...args)`  
`expect(spy).to.be.calledWithExactly(...args)` |
| alwaysCalledWithExactly | `.should('have.always.been.calledWithExactly', ...args)`  
`expect(spy).to.always.be.calledWithExactly(...args)` |
| calledOnceWithExactly | `.should('have.been.calledOnceWithExactly', ...args)`  
`expect(spy).to.be.calledOnceWithExactly(...args)` |
| calledWithMatch | `.should('have.been.calledWithMatch',...args)`  
`expect(spy).to.be.calledWithMatch(...args)` |
| alwaysCalledWithMatch | `.should('have.always.been.calledWithMatch',...args)`  
`expect(spy).to.always.be.calledWithMatch(...args)` |
| returned | `.should('have.returned', 'foo')`  
`expect(spy).to.have.returned(returnVal)` |
| alwaysReturned | `.should('have.always.returned', 'foo')`  
`expect(spy).to.have.always.returned(returnVal)` |
| threw | `.should('have.thrown', TypeError)`  
`expect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing)` |
| alwaysThrew | `.should('have.always.thrown', 'TypeError')`  
`expect(spy).to.have.always.thrown(errorObjOrErrorTypeStringOrNothing)` |

## Adding New Assertions

Because we are using `chai`, that means you can extend it however you'd like. Cypress will "just work" with new assertions added to `chai`. You can:

*   Write your own `chai` assertions as [documented here](https://chaijs.com/guide/helpers/).
*   npm install any existing `chai` library and import into your test file or [support file](/llm/markdown/app/core-concepts/writing-and-organizing-tests.md#Support-file).

[Check out our example recipe extending chai with new assertions.](/llm/markdown/app/references/recipes.md#Fundamentals)

## Common Assertions

Here is a list of common element assertions. Notice how we use these assertions (listed above) with [`.should()`](/llm/markdown/api/commands/should.md). You may also want to read about how Cypress [retries](/llm/markdown/app/core-concepts/retry-ability.md) assertions.

### Length

```
// retry until we find 3 matching <li.selected>cy.get('li.selected').should('have.length', 3)
```

### Class

```
// retry until this input does not have class disabledcy.get('form').find('input').should('not.have.class', 'disabled')
```

### Value

```
// retry until this textarea has the correct valuecy.get('textarea').should('have.value', 'foo bar baz')
```

### Text Content

```
// assert the element's text content is exactly the given textcy.get('[data-testid="user-name"]').should('have.text', 'Joe Smith')// assert the element's text includes the given substringcy.get('[data-testid="address"]').should('include.text', 'Atlanta')// retry until this span does not contain 'click me'cy.get('a').parent('span.help').should('not.contain', 'click me')// the element's text should start with "Hello"cy.get('[data-testid="greeting"]')  .invoke('text')  .should('match', /^Hello/)// use cy.contains to find an element with its text// matching the given regular expressioncy.contains('[data-testid="greeting"]', /^Hello/)
```

**Tip:** read about assertions against text with non-breaking space entities in [How do I get an element's text contents?](/llm/markdown/app/faq.md#How-do-I-get-an-elements-text-contents)

### Visibility

```
// retry until the element with// data-testid "form-submit" is visiblecy.get('[data-testid="form-submit"]').should('be.visible')// retry until the list item with// text "write tests" is visiblecy.contains('[data-testid="todo"] li', 'write tests').should('be.visible')
```

**Note:** if there are multiple elements, the assertions `be.visible` and `not.be.visible` act differently:

```
// retry until SOME elements are visiblecy.get('li').should('be.visible')// retry until EVERY element is invisiblecy.get('li.hidden').should('not.be.visible')
```

Watch the short video ["Multiple elements and should('be.visible') assertion"](https://www.youtube.com/watch?v=LxkrhUEE2Qk) that shows how to correctly check the visibility of elements.

**Visibility Semantics**

For detailed information about how Cypress determines element visibility, including the default behavior and the experimental fast visibility algorithm, see [Visibility](/llm/markdown/app/core-concepts/interacting-with-elements.md#Visibility).

### Existence

```
// retry until loading spinner no longer existscy.get('[data-testid="loading"]').should('not.exist')
```

### State

```
// retry until our radio is checkedcy.get(':radio').should('be.checked')
```

### CSS

```
// retry until element has matching csscy.get('[data-testid="completed"]').should(  'have.css',  'text-decoration',  'line-through')
```

```
// retry while accordion css has the// "display: none" propertycy.get('[data-testid="accordion"]').should('not.have.css', 'display', 'none')
```

### Disabled property

```
<input type="text" data-testid="example-input" disabled />
```

```
cy.get('[data-testid="example-input"]')  .should('be.disabled')  // let's enable this element from the test  .invoke('prop', 'disabled', false)cy.get('[data-testid="example-input"]')  // we can use "enabled" assertion  .should('be.enabled')  // or negate the "disabled" assertion  .and('not.be.disabled')
```

## Negative assertions

There are positive and negative assertions. Examples of positive assertions are:

```
cy.get('[data-testid="todo-item"]')  .should('have.length', 2)  .and('have.class', 'completed')
```

The negative assertions have the "not" chainer prefixed to the assertion. Examples of negative assertions are:

```
cy.contains('first todo').should('not.have.class', 'completed')cy.get('[data-testid="loading"]').should('not.be.visible')
```

### False passing tests

Negative assertions may pass for reasons you weren't expecting. Let's say we want to test that a Todo list app adds a new Todo item after typing the Todo and pressing enter.

**Positive assertions**

When adding an element to the list and using a **positive assertion**, the test asserts a specific number of Todo items in our application.

The test below may still falsely pass if the application behaves unexpectedly, like adding a blank Todo, instead of adding the new Todo with the text "Write tests".

```
cy.get('[data-testid="todos"]').should('have.length', 2)cy.get('[data-testid="new-todo"]').type('Write tests{enter}')// using a positive assertion to check the// exact number of itemscy.get('[data-testid="todos"]').should('have.length', 3)
```

**Negative assertions**

But when using a **negative assertion** in the test below, the test can falsely pass when the application behaves in multiple unexpected ways:

*   The app deletes the entire list of Todo items instead of inserting the 3rd Todo
*   The app deletes a Todo instead of adding a new Todo
*   The app adds a blank Todo

```
cy.get('[data-testid="todos"]').should('have.length', 2)cy.get('[data-testid="new-todo"]').type('Write tests{enter}')// using negative assertion to check it's// not a number of itemscy.get('[data-testid="todos"]').should('not.have.length', 2)
```

## Should callback

If built-in assertions are not enough, you can write your own assertion function and pass it as a callback to the `.should()` command. Cypress will automatically [retry](/llm/markdown/app/core-concepts/retry-ability.md) the callback function until it passes or the command times out. See the [`.should()`](/llm/markdown/api/commands/should.md#Function) documentation.

```
<div class="main-abc123 heading-xyz987">Introduction</div>
```

```
cy.get('div').should(($div) => {  expect($div).to.have.length(1)  const className = $div[0].className  // className will be a string like "main-abc123 heading-xyz987"  expect(className).to.match(/heading-/)})
```

## Multiple assertions

You can attach multiple assertions to the same command.

```
<a  data-testid="assertions-link"  class="active"  href="https://on.cypress.io"  target="_blank">  Cypress Docs</a>
```

```
cy.get('[data-testid="assertions-link"]')  .should('have.class', 'active')  .and('have.attr', 'href')  .and('include', 'cypress.io')
```

Note that all chained assertions will use the same reference to the original subject. For example, if you wanted to test a loading element that first appears and then disappears, the following WILL NOT WORK because the same element cannot be visible and invisible at the same time:

```
// ⛔️ DOES NOT WORKcy.get('[data-testid="loading"]').should('be.visible').and('not.be.visible')
```

Instead you should split the assertions and re-query the element:

```
// ✅ THE CORRECT WAYcy.get('[data-testid="loading"]').should('be.visible')cy.get('[data-testid="loading"]').should('not.be.visible')
```

## See also

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