Assertions
Article Summary
Assertions allow you to compare the actual state of your application to your expectations. Assertions are necessary to conclusively determine test success.
Assertions begin with the invocation of the t.expect method. The following simple assertion compares variable x to variable y and succeeds if the two are equal:
await t.expect(x).eql(y);
The left side of the assertion can contain asynchronous functions that extract information from the page. TestCafe offers a set of assertion methods that compare and evaluate assertion operands. TestCafe employs the Smart Assertion Query Mechanism to eliminate false negatives.
Table of Contents
- Why use assertions
- Assertion structure
- Use assertions to extract page information
- Assertion methods
- Assertion options
- How Assertions Work
- Debug Assertions
Why use assertions
Tests without an explicit success condition are inconclusive.
If one of the following errors occurs, the test automatically fails:
- TestCafe cannot reach the test page URL.
- TestCafe cannot perform a test action — for example, if the target element does not exist.
- Your website throws a JavaScript error.
But you still don’t know if test actions have had the desired effect. That’s why you should add assertions (custom success conditions) to your test.
Examples
If your test includes a log-in routine, you may use assertions to perform the following checks:
Log-in status check
Confirm that the log-in routine has been successful. Check the page for items that are invisible to unauthenticated users:
await t.expect(Selector('#account-preferences').filterVisible().exists).ok();
User check
Check that the page displays the correct username.
await t.expect(Selector('#user-name').innerText).contains('Jane Doe');
Cookies check
Confirm that the user received the cookies necessary to continue the session:
let cookies = await t.getCookies({ domain: 'yourwebsite.com' }); await t.expect(cookies.length).eql(2);
Assertion structure
The following simple assertion compares variable x to variable y and succeeds if the two are equal:
This assertion consists of the following parts:
The assertion declaration
Assertions start with the invocation of the t.expect
method. Assertions are asynchronous methods, and require the use of the await
keyword.
You can declare an assertion in any function that has access to the Test Controller, such as the test body, test hooks, and Role definitions. Manually import the TestController object to declare assertions elsewhere.
The first operand
The first operand indicates the actual state of the application. It can contain an asynchronous function that extracts page information:
await t.expect(Selector('#article-header').innerText).contains('10 Best Vacation Spots');
When you pass a compatible function to the assertion, TestCafe engages the Smart Assertion Query Mechanism. If the function fails the first time, TestCafe executes it again to account for possible client-side changes.
The assertion method
The assertion method determines the nature of the comparison. For example. the eql
assertion method succeeds when the two operands are equal. See the assertion methods section for an overview of available assertion methods.
The second operand
The second operand indicates the expected value of the assertion’s first operand. The second operand of the assertion cannot contain a function.
How to chain assertions
You can chain assertions with other TestController methods:
// TestCafe executes the 'click' action after the assertion:
t.expect(x).eql(y).click('#button');
You can only chain complete assertions:
// The test fails to start because the assertion is incomplete:
t.expect(x).click('#button');
Use assertions to extract page information
To extract information from the page, pass an asynchronous function to the first operand.
If the function fails, TestCafe retries it multiple times within the assertion timeout. See Smart Assertion Query Mechanism for more information.
TestCafe automatically awaits the following functions:
-
const getLocationPart = ClientFunction(locationPart => { return window.location[locationPart]; }); await t.expect(getLocationPart('host')).eql('devexpress.github.io');
Selector property invocations
await t.expect(Selector('#article-header').innerText).contains('10 Best Vacation Spots');
Two RequestLogger methods — count and contains
import { RequestLogger } from 'testcafe'; const logger = RequestLogger(); fixture('RequestLogger') .page('https://devexpress.github.io/testcafe/example/') .requestHooks(logger); test('Check request', async t => { await t.expect(logger.count()).ok(); await t.expect(logger.contains(record => record.response.statusCode === 200)).ok(); });
-
const responseBody = t.request('http://localhost:3000/helloworld').body; await t.expect(responseBody).contains('Hello World');
Common Errors and Best Practices
Do not place Selector queries without properties into the left-hand side of the assertion:
test('test', async t => { await t .typeText('input', '123') .expect(Selector('input')).contains('123'); // fails });
If you place the
await
keyword into the left-hand side of the assertion, TestCafe does not enage the Smart Assertion Query Mechanism.test('test', async t => { await t .typeText('input', '123') .expect(await Selector('input').value).eql('123'); });
If you pass the result of Selector evaluation to the assertion, TestCafe does not enage the Smart Assertion Query Mechanism.
test('test', async t => { const buttonValue = await Selector('#btn').textContent; // The constant stores the return value of the Selector query. await t.expect(buttonValue).contains('Loading...'); // This assertion compares two static values. if the assertion fails, TestCafe does not retry it, because the result would not change. await t.expect(Selector('#btn').textContent).contains('Loading...'); // This assertion contians a proper Selector query. TestCafe retries the assertion in case of failure. });
Do not place a function that returns a promise in the left hand-side of the assertion.
Assertion methods
TestCafe offers a comprehensive set of assertion methods.
Different assertion methods require different arguments. For example, the within
method requires two numeric arguments. The ok
and notOk
methods are Boolean, and do not require an argument. Refer to the documentation of a specific assertion method for more information.
We can divide assertion methods into groups based on their mathematical function:
- Strict equality
- Value comparison
- Superset
- Numeric range check
- Truthiness check
- Type check
- Regular expression check
Strict equality
The following assertion methods perform a strict equality check:
eql (X = Y)
Method reference: eql
Examples:
// successful assertions:
await t.expect(20).eql(20);
await t.expect('20').eql('20');
await t.expect({ username: 'steve@example.com' }).eq({ username: 'steve@example.com' });
// failed assertions:
await t.expect(15).eql(20);
await t.expect('20').eql('15');
await t.expect({ username: 'steve@example.com' }).eq({ username: 'dave@example.com' });
// invalid assertions:
await t.expect('20').eql(20); // operand data types don't match
notEql (X ≠ Y)
Method reference: notEql
Examples:
// successful assertions:
await t.expect(15).notEql(20);
await t.expect('20').notEql('15');
await t.expect({ username: 'steve@example.com' })notEql({ username: 'dave@example.com' });
// failed assertions:
await t.expect(20).notEql(20);
await t.expect('20').notEql('20');
await t.expect({ username: 'steve@example.com' })notEql({ username: 'steve@example.com' });
// invalid assertions:
await t.expect('20').notEql(20); // operand data types don't match
Value comparison
The following assertion methods compare two numeric values:
gt (X > Y)
Method reference: gt
Examples:
const today = new Date(); // current date
const lovelessReleaseDate = new Date(1991, 10, 4); // November 4th, 1991
// successful assertions:
await t.expect(20).gt(15)
await t.expect(today).gt(lovelessReleaseDate);
// failed assertions:
await t.expect(15).gt(15)
await t.expect(lovelessReleaseDate).gt(today);
// invalid assertions:
await t.expect('16').gt(15); // invalid data type (string)
await t.expect(today).gt(15); // operand data types don't match
gte (X ≥ Y)
Method reference: gte
Examples:
const today = new Date(); // current date
const lovelessReleaseDate = new Date(1991, 10, 4); // November 4th, 1991
// successful assertions:
await t.expect(20).gte(15);
await t.expect(15).gte(15);
await t.expect(today).gte(lovelessReleaseDate);
await t.expect(today).gte(today);
// failed assertions:
await t.expect(14).gte(15);
await t.expect(lovelessReleaseDate).gte(today);
// invalid assertions:
await t.expect('16').gte(15); // invalid data type (string)
await t.expect(today).gte(15); // operand data types don't match
lte (X ≤ Y)
Method reference: lte
Examples:
const today = new Date(); // current date
const lovelessReleaseDate = new Date(1991, 10, 4); // November 4th, 1991
// successful assertions:
await t.expect(14).lte(15);
await t.expect(15).lte(15);
await t.expect(lovelessReleaseDate).lte(today);
await t.expect(today).lte(today);
// failed assertions:
await t.expect(20).lte(15);
await t.expect(today).lte(lovelessReleaseDate);
// invalid assertions:
await t.expect('14').lte(15) // invalid data type (string)
await t.expect(today).lte(15) // operand data types don't match
lt (X < Y)
Method reference: lt
const today = new Date(); // current date
const lovelessReleaseDate = new Date(1991, 10, 4); // November 4th, 1991
// successful assertions:
await t.expect(14).lt(15)
await t.expect(lovelessReleaseDate).lt(today);
// failed assertions:
await t.expect(15).lt(15)
await t.expect(today).lt(lovelessReleaseDate);
// invalid assertions:
await t.expect('14').lt(15); // invalid data type (string)
await t.expect(today).lt(15); // operand data types don't match
Superset
The following assertion methods check whether X includes Y:
contains (Y ∈ X)
Method reference: contains
Examples:
// successful assertions:
await t.expect(['x','y']).contains('y');
await t.expect('Username: steve@example.com').contains('Username');
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).contains({ username: 'steve@example.com' });
// failed assertions:
await t.expect(['x','y']).contains('z');
await t.expect('Username: steve@example.com').contains('Password');
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).contains({ username: 'dave@example.com' });
// invalid assertions:
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).contains('steve@example.com'); // operand data types don't match
notContains (Y ∉ X)
Method reference: notContains
Examples:
// successful assertions:
await t.expect(['x','y']).notContains('z');
await t.expect('Username: steve@example.com').notContains('Password');
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).notContains({ username: 'dave@example.com' });
// failed assertions:
await t.expect(['x','y']).notContains('y');
await t.expect('Username: steve@example.com').notContains('Username');
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).notContains({ username: 'steve@example.com' });
// invalid assertions:
await t.expect({ username: 'steve@example.com', subscriptionPlan: 'basic' }).notContains('dave@example.com'); // operand data types don't match
Numeric range check
The following assertion methods check whether the Y range of numbers includes X.
within (X ∈ [Yᵃ, Yᵇ])
Method reference: within
Examples:
await t.expect(10).within(1, 20); // success
await t.expect(22).within(1, 20); // failure
// invalid assertions:
await t.expect(22).within([20, 100]); // the second operand is an array
await t.expect(22).within(1); // insufficient number of range definition arguments
await t.expect(10).within(20, 1); // invalid range definition: 20 is greater than 1
notWithin (X ∉ [Yᵃ, Yᵇ])
Method reference:notWithin
Examples:
await t.expect(22).notWithin(1, 20); // success
await t.expect(10).notWithin(1, 20); // failure
// invalid assertions:
await t.expect(12).notWithin([20, 100]); // the second operand is an array
await t.expect(22).notWithin(1); // insufficient number of range definition arguments
await t.expect(22).notWithin(20, 1); // invalid range definition: 20 is greater than 1
Truthiness check
The following assertion methods check the Boolean value of X.
ok (⊤(X))
Method reference: ok
const inputField = Selector('#developer-name');
const fakeElement = Selector('#this-element-does-not-exist');
// successful assertions:
await t.expect(inputField.exists).ok();
await t.expect('Hello!').ok();
// failed assertions:
await t.expect(fakeElement.exists).ok(); // the element doesn't exist
await t.expect('').ok(); // the string is empty
await t.expect(null).ok(); // null isn't truthy
await t.expect(undefined).ok(); // undefined isn't truthy
notOK (⊥(X))
Method reference: notOk
Examples:
const inputField = Selector('#developer-name');
const fakeElement = Selector('#this-element-does-not-exist');
// successful assertions:
await t.expect(fakeElement.exists).notOk(); // the element doesn't exist
await t.expect('').notOk(); // the string is empty
await t.expect(null).notOk(); // null is falsy
await t.expect(undefined).notOk(); // undefined is falsy
// failed assertions:
await t.expect(inputField.exists).notOk();
await t.expect('Hello!').notOk();
Type check
The following assertion methods check whether X belongs to data type Y.
typeOf (X ∈ type Y)
Method reference: typeOf
Examples:
await t.expect(12).typeOf('Number'); // success
await t.expect(12).typeOf('Object'); // failure
notTypeOf (X ∉ type Y)
Method reference: notTypeOf
Examples:
await t.expect(12).notTypeOf('Object'); // success
await t.expect(12).notTypeOf('Number'); // failure
Regular expression check
The following assertion methods check whether X matches the regular expression Y:
match
Method reference: match
X matches the regular expression Y.
Examples:
const emailRegex = new RegExp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
await t.expect('email@email.com').match(emailRegex); // success
await t.expect('email@email.com').match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/); // success
await t.expect('email.com').match(emailRegex); // failure
notMatch
Method reference: notMatch
X does not match the regular expression Y.
Examples:
const emailRegex = new RegExp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
await t.expect('email.com').notMatch(emailRegex); // success
await t.expect('email.com').notMatch(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/); // success
await t.expect('email@email.com').notMatch(emailRegex); // failure
Assertion options
Custom error message
Type: String
You can define a custom error message for the assertion. Pass the string with the message to the assertion method:
await t.expect({ a: 'bar' }).eql({ a: 'foo' }, 'this assertion will fail');
Assertion timeout
Type: Number
If an assertion’s first operand contains a compatible function, the assertion is subject to the Smart Assertion Query Mechanism.
If such an assertion fails, TestCafe executes it again until it meets either of the following criteria:
- The assertion succeeds.
- The assertion timeout elapses.
await t.expect(Selector('h1').innerText).eql('text', 'check element text', { timeout: 20000 });
To set the timeout for the entire test run, define the assertion timeout in one of the following ways:
- Set the assertionTimeout configuration file option.
- Set the assertion-timeout CLI option.
- Set the assertionTimeout Runner API option.
Note
The timeout
option applies to built-in TestCafe assertion methods. Use the t.wait() method to specify timeouts for third-party assertion methods (assert or chai).
Allow Unawaited Promise
TestCafe awaits Promises from compatible asynchronous functions. If your assertion includes a custom function that returns a Promise, the assertion fails.
When you create assertions, avoid the use of custom functions that return a Promise. If you can’t work around this limitation, use the allowUnawaitedPromise
option:
await t
.expect(new Promise(resolve => setTimeout(resolve, 100)))
.ok('received a promise', { allowUnawaitedPromise: true });
How Assertions Work
Functional web tests are fundamentally asynchronous. It takes time for the application to respond to user actions.
The application’s reponse speed depends on factors such as the speed of the network, the speed of the database, and front-end animations. Since the response is not immediate, it is often impossible to execute assertions right after the action ends.
Traditional end-to-end frameworks solve this issue with an extra timeout:
TestCafe improves on this approach with its Smart Assertion Query Mechanism.
Smart Assertion Query Mechanism
If the first operand of an assertion is a compatible asynchronous function — for example, a Selector property — TestCafe retries the assertion multiple times within the assertion timeout.
The timeout option sets the timeout value on a per-assertion basis. To set the timeout for the entire test run, pass the timeout value to the assertion-timeout CLI option or the runner.run option (Test Runner API).
The test fails if the assertion doesn’t succeed by the end of the timeout period.
Example:
Consider the following HTML page:
<html>
<body>
<div id="btn" onclick="window.setTimeout(() => this.textContent = 'Loading...', 100)">Click me!</div>
</body>
</html>
When the user clicks the #btn
element, the browser initiates a 100ms wait period. When the wait period elapses, the browser changes the button’s content.
A regular assertion would fail because the application does not respond instantly. TestCafe accounts for the possibility of a slow reponse, and the following assertion succeeds:
test('Button click', async t => {
const btn = Selector('#btn');
await t
.click(btn)
.expect(btn.textContent).contains('Loading...');
});
Debug Assertions
Assertions may fail thanks to a badly written Element Selector query or a short assertion timeout. Read the Debug Tests guide for more information.