Assertions
Cypress bundles the popular Chai assertion library, as well as helpful extensions for Sinon and jQuery, bringing you dozens of powerful assertions for free.
info
This document is only a reference to every assertion Cypress supports.
If you're looking to understand how to use these assertions please read about assertions in our Introduction to Cypress guide.
Chai
[https://github.com/chaijs/chai](https://github.com/chaijs/chai)BDD Assertions
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.
Chainer | Example |
---|---|
not | expect(name).to.not.equal('Jane') |
deep | expect(obj).to.deep.equal({ name: 'Jane' }) |
nested | expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]') expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}) |
ordered | expect([1, 2]).to.have.ordered.members([1, 2]).but.not.have.ordered.members([2, 1]) |
any | expect(arr).to.have.any.keys('age') |
all | expect(arr).to.have.all.keys('name', 'age') |
a(type) Aliases: an | expect('test').to.be.a('string') |
include(value) Aliases: contain, includes, contains | expect([1,2,3]).to.include(2) |
ok | expect(undefined).to.not.be.ok |
true | expect(true).to.be.true |
false | expect(false).to.be.false |
null | expect(null).to.be.null |
undefined | expect(undefined).to.be.undefined |
exist | expect(myVar).to.exist |
empty | expect([]).to.be.empty |
arguments Aliases: Arguments | expect(arguments).to.be.arguments |
equal(value) Aliases: equals, eq | expect(42).to.equal(42) |
deep.equal(value) | expect({ name: 'Jane' }).to.deep.equal({ name: 'Jane' }) |
eql(value) Aliases: eqls | expect({ name: 'Jane' }).to.eql({ name: 'Jane' }) |
greaterThan(value) Aliases: gt, above | expect(10).to.be.greaterThan(5) |
least(value) Aliases: gte | expect(10).to.be.at.least(10) |
lessThan(value) Aliases: lt, below | expect(5).to.be.lessThan(10) |
most(value) Aliases: lte | expect('test').to.have.length.of.at.most(4) |
within(start, finish) | expect(7).to.be.within(5,10) |
instanceOf(constructor) Aliases: instanceof | expect([1, 2, 3]).to.be.instanceOf(Array) |
property(name, [value]) | expect(obj).to.have.property('name') |
deep.property(name, [value]) | expect(deepObj).to.have.deep.property('tests[1]', 'e2e') |
ownProperty(name) Aliases: haveOwnProperty, own.property | expect('test').to.have.ownProperty('length') |
ownPropertyDescriptor(name) Aliases: haveOwnPropertyDescriptor | expect({a: 1}).to.have.ownPropertyDescriptor('a') |
lengthOf(value) | expect('test').to.have.lengthOf(3) |
match(RegExp) Aliases: matches | expect('testing').to.match(/^test/) |
string(string) | expect('testing').to.have.string('test') |
keys(key1, [key2], [...]) Aliases: key | expect({ pass: 1, fail: 2 }).to.have.keys('pass', 'fail') |
throw(constructor) Aliases: throws, Throw | expect(fn).to.throw(Error) |
respondTo(method) Aliases: respondsTo | expect(obj).to.respondTo('getName') |
itself | expect(Foo).itself.to.respondTo('bar') |
satisfy(method) Aliases: satisfies | expect(1).to.satisfy((num) => { return num > 0 }) |
closeTo(expected, delta) Aliases: approximately | expect(1.5).to.be.closeTo(1, 0.5) |
members(set) | expect([1, 2, 3]).to.include.members([3, 2]) |
oneOf(values) | expect(2).to.be.oneOf([1,2,3]) |
change(function) Aliases: changes | expect(fn).to.change(obj, 'val') |
increase(function) Aliases: increases | expect(fn).to.increase(obj, 'val') |
decrease(function) Aliases: decreases | 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 |
TDD Assertions
These assertions are available for TDD assertions (assert
). You can see the
entire list of available Chai assertions here.
Assertion | Example |
---|---|
.isOk(object, [message]) | assert.isOk('everything', 'everything is ok') |
.isNotOk(object, [message]) | assert.isNotOk(false, 'this will pass') |
.equal(actual, expected, [message]) | assert.equal(3, 3, 'vals equal') |
.notEqual(actual, expected, [message]) | assert.notEqual(3, 4, 'vals not equal') |
.strictEqual(actual, expected, [message]) | assert.strictEqual(true, true, 'bools strict eq') |
.notStrictEqual(actual, expected, [message]) | assert.notStrictEqual(5, '5', 'not strict eq') |
.deepEqual(actual, expected, [message]) | assert.deepEqual({ id: '1' }, { id: '1' }) |
.notDeepEqual(actual, expected, [message]) | assert.notDeepEqual({ id: '1' }, { id: '2' }) |
.isAbove(valueToCheck, valueToBeAbove, [message]) | assert.isAbove(6, 1, '6 greater than 1') |
.isAtLeast(valueToCheck, valueToBeAtLeast, [message]) | assert.isAtLeast(5, 2, '5 gt or eq to 2') |
.isBelow(valueToCheck, valueToBeBelow, [message]) | assert.isBelow(3, 6, '3 strict lt 6') |
.isAtMost(valueToCheck, valueToBeAtMost, [message]) | assert.isAtMost(4, 4, '4 lt or eq to 4') |
.isTrue(value, [message]) | assert.isTrue(true, 'this val is true') |
.isNotTrue(value, [message]) | assert.isNotTrue('tests are no fun', 'val not true') |
.isFalse(value, [message]) | assert.isFalse(false, 'val is false') |
.isNotFalse(value, [message]) | assert.isNotFalse('tests are fun', 'val not false') |
.isNull(value, [message]) | assert.isNull(err, 'there was no error') |
.isNotNull(value, [message]) | assert.isNotNull('hello', 'is not null') |
.isNaN(value, [message]) | assert.isNaN(NaN, 'NaN is NaN') |
.isNotNaN(value, [message]) | assert.isNotNaN(5, '5 is not NaN') |
.exists(value, [message]) | assert.exists(5, '5 is not null or undefined') |
.notExists(value, [message]) | assert.notExists(null, 'val is null or undefined') |
.isUndefined(value, [message]) | assert.isUndefined(undefined, 'val is undefined') |
.isDefined(value, [message]) | assert.isDefined('hello', 'val has been defined') |
.isFunction(value, [message]) | assert.isFunction(x => x * x, 'val is func') |
.isNotFunction(value, [message]) | assert.isNotFunction(5, 'val not funct') |
.isObject(value, [message]) | assert.isObject({num: 5}, 'val is object') |
.isNotObject(value, [message]) | assert.isNotObject(3, 'val not object') |
.isArray(value, [message]) | assert.isArray(['unit', 'e2e'], 'val is array') |
.isNotArray(value, [message]) | assert.isNotArray('e2e', 'val not array') |
.isString(value, [message]) | assert.isString('e2e', 'val is string') |
.isNotString(value, [message]) | assert.isNotString(2, 'val not string') |
.isNumber(value, [message]) | assert.isNumber(2, 'val is number') |
.isNotNumber(value, [message]) | assert.isNotNumber('e2e', 'val not number') |
.isFinite(value, [message]) | assert.isFinite('e2e', 'val is finite') |
.isBoolean(value, [message]) | assert.isBoolean(true, 'val is bool') |
.isNotBoolean(value, [message]) | assert.isNotBoolean('true', 'val not bool') |
.typeOf(value, name, [message]) | assert.typeOf('e2e', 'string', 'val is string') |
.notTypeOf(value, name, [message]) | assert.notTypeOf('e2e', 'number', 'val not number') |
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()
, cy.contains()
, etc.
Chainers | Assertion |
---|---|
attr(name, [value]) | expect($el).to.have.attr('foo', 'bar') |
prop(name, [value]) | expect($el).to.have.prop('disabled', false) |
css(name, [value]) | expect($el).to.have.css('background-color', 'rgb(0, 0, 0)') |
data(name, [value]) | expect($el).to.have.data('foo', 'bar') |
class(className) | expect($el).to.have.class('foo') |
id(id) | expect($el).to.have.id('foo') |
html(html) | expect($el).to.have.html('I love testing') |
text(text) | expect($el).to.have.text('I love testing') |
value(value) | expect($el).to.have.value('test@dev.com') |
visible | expect($el).to.be.visible |
hidden | expect($el).to.be.hidden |
selected | expect($option).not.to.be.selected |
checked | expect($input).not.to.be.checked |
focus[ed] | expect($input).not.to.be.focused expect($input).to.have.focus |
enabled | expect($input).to.be.enabled |
disabled | expect($input).to.be.disabled |
empty | expect($el).not.to.be.empty |
exist | expect($nonexistent).not.to.exist |
match(selector) | expect($emptyEl).to.match(':empty') |
contain(text) | expect($el).to.contain('text') |
descendants(selector) | 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()
and
cy.spy()
.
Sinon.JS property/method | Assertion |
---|---|
called | expect(spy).to.be.called |
callCount | expect(spy).to.have.callCount(n) |
calledOnce | expect(spy).to.be.calledOnce |
calledTwice | expect(spy).to.be.calledTwice |
calledThrice | expect(spy).to.be.calledThrice |
calledBefore | expect(spy1).to.be.calledBefore(spy2) |
calledAfter | expect(spy1).to.be.calledAfter(spy2) |
calledWithNew | expect(spy).to.be.calledWithNew |
alwaysCalledWithNew | expect(spy).to.always.be.calledWithNew |
calledOn | expect(spy).to.be.calledOn(context) |
alwaysCalledOn | expect(spy).to.always.be.calledOn(context) |
calledWith | expect(spy).to.be.calledWith(...args) |
alwaysCalledWith | expect(spy).to.always.be.calledWith(...args) |
calledOnceWith | expect(spy).to.be.calledOnceWith(...args) |
calledWithExactly | expect(spy).to.be.calledWithExactly(...args) |
alwaysCalledWithExactly | expect(spy).to.always.be.calledWithExactly(...args) |
calledOnceWithExactly | expect(spy).to.be.calledOnceWithExactly(...args) |
calledWithMatch | expect(spy).to.be.calledWithMatch(...args) |
alwaysCalledWithMatch | expect(spy).to.always.be.calledWithMatch(...args) |
returned | expect(spy).to.have.returned(returnVal) |
alwaysReturned | expect(spy).to.have.always.returned(returnVal) |
threw | expect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing) |
alwaysThrew | 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. - npm install any existing
chai
library and import into your test file or support file.
Common Assertions
Here is a list of common element assertions. Notice how we use these assertions
(listed above) with .should()
. You may also want to
read about how Cypress retries
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 disabled
cy.get("form").find("input").should("not.have.class", "disabled");
Value
// retry until this textarea has the correct value
cy.get("textarea").should("have.value", "foo bar baz");
Text Content
// assert the element's text content is exactly the given text
cy.get('[data-testid="user-name"]').should("have.text", "Joe Smith");
// assert the element's text includes the given substring
cy.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 expression
cy.contains('[data-testid="greeting"]', /^Hello/);
info
Tip: read about assertions against text with non-breaking space entities in How do I get an element's text contents?
Visibility
// retry until the element with
// data-testid "form-submit" is visible
cy.get('[data-testid="form-submit"]').should("be.visible");
// retry until the list item with
// text "write tests" is visible
cy.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 visible
cy.get("li").should("be.visible");
// retry until EVERY element is invisible
cy.get("li.hidden").should("not.be.visible");
Watch the short video "Multiple elements and should('be.visible') assertion" that shows how to correctly check the visibility of elements.
Existence
// retry until loading spinner no longer exists
cy.get('[data-testid="loading"]').should("not.exist");
State
// retry until our radio is checked
cy.get(":radio").should("be.checked");
CSS
// retry until element has matching css
cy.get('[data-testid="completed"]').should(
"have.css",
"text-decoration",
"line-through"
);
// retry while accordion css has the
// "display: none" property
cy.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 items
cy.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
- An infinite variety of possible application mistakes
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 items
cy.get('[data-testid="todos"]').should("not.have.length", 2);
Recommendation
We recommend using negative assertions to verify that a specific condition is no longer present after the application performs an action. For example, when a previously completed item is unchecked, we might verify that a CSS class is removed.
// at first the item is marked completed
cy.contains('[data-testid="todos"]', "Write tests")
.should("have.class", "completed")
.find('[data-testid="toggle"]')
.click();
// the CSS class has been removed
cy.contains('[data-testid="todos"]', "Write tests").should(
"not.have.class",
"completed"
);
info
For more examples, please read the blog post Be Careful With Negative Assertions.
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 the callback function until it
passes or the command times out. See the
.should()
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 WORK
cy.get('[data-testid="loading"]').should("be.visible").and("not.be.visible");
Instead you should split the assertions and re-query the element:
// ✅ THE CORRECT WAY
cy.get('[data-testid="loading"]').should("be.visible");
cy.get('[data-testid="loading"]').should("not.be.visible");