What is wait-for-expect?
The wait-for-expect npm package is a utility for writing asynchronous tests. It allows you to wait for certain conditions to be met before proceeding with your test assertions, making it particularly useful for testing asynchronous code such as API calls, timers, and UI updates.
What are wait-for-expect's main functionalities?
Basic Usage
This feature allows you to wait for a condition to be met within a specified timeout. In this example, the test waits for the value to become 1 within 1000 milliseconds.
const waitForExpect = require('wait-for-expect');
let value = 0;
setTimeout(() => { value = 1; }, 500);
await waitForExpect(() => {
expect(value).toBe(1);
}, 1000);
Custom Interval
This feature allows you to specify a custom interval for checking the condition. In this example, the condition is checked every 100 milliseconds within a 1000 milliseconds timeout.
const waitForExpect = require('wait-for-expect');
let value = 0;
setTimeout(() => { value = 1; }, 500);
await waitForExpect(() => {
expect(value).toBe(1);
}, 1000, 100);
Handling Promises
This feature demonstrates how to handle promises with wait-for-expect. The test waits for the async function to resolve and then checks the condition within the specified timeout.
const waitForExpect = require('wait-for-expect');
let value = 0;
const asyncFunction = () => new Promise(resolve => setTimeout(() => { value = 1; resolve(); }, 500));
await asyncFunction();
await waitForExpect(() => {
expect(value).toBe(1);
}, 1000);
Other packages similar to wait-for-expect
jest
Jest is a popular testing framework that includes built-in support for asynchronous tests. It provides functions like `waitFor` and `waitForElement` to handle asynchronous code, making it a comprehensive solution for testing. Compared to wait-for-expect, Jest offers a more extensive set of features but may be overkill for simple use cases.
testing-library__react
React Testing Library is a lightweight solution for testing React components. It includes a `waitFor` utility that serves a similar purpose to wait-for-expect, allowing you to wait for conditions to be met in your tests. It is more specialized for React applications compared to the more general-purpose wait-for-expect.
chai-as-promised
Chai as Promised extends the Chai assertion library to handle promises. It allows you to write assertions that wait for promises to resolve, similar to wait-for-expect. However, it is more focused on promise-based assertions rather than general asynchronous conditions.
wait-for-expect
Wait for expectation to be true, useful for integration and end to end testing
Think things like calling external APIs, database operations, or even GraphQL subscriptions.
We will add examples for all of them soon, for now please enjoy the simple docs. :-)
Usage:
const waitForExpect = require("wait-for-expect")
test("it waits for the number to change", async () => {
let numberToChange = 10;
const randomTimeout = Math.floor(Math.random() * 300);
setTimeout(() => {
numberToChange = 100;
}, randomTimeout);
await waitForExpect(() => {
expect(numberToChange).toEqual(100);
});
});
instead of:
test("it waits for the number to change", () => {
let numberToChange = 10;
const randomTimeout = Math.floor(Math.random() * 300);
setTimeout(() => {
numberToChange = 100;
}, randomTimeout);
setTimeout(() => {
expect(numberToChange).toEqual(100);
}, 700);
});
It will check whether the expectation passes right away in the next available "tick" (very useful with, for example, integration testing of react when mocking fetches, like here: https://github.com/kentcdodds/react-testing-library#usage).
If it doesn't, it will keep repeating for the duration of, at most, the specified timeout, every 50 ms. The default timeout is 4.5 seconds to fit below the default 5 seconds that Jest waits for before throwing an error.
Nice thing about this simple tool is that if the expectation keeps failing till the timeout, it will check it one last time, but this time the same way your test runner would run it - so you basically get your expectation library error, the sam way like if you used setTimeout to wait but didn't wait long enough.
To show an example - if I change the expectation to wait for 105 in above code, you will get nice and familiar:
FAIL src/waitForExpect.spec.js (5.042s)
✕ it waits for the number to change (4511ms)
● it waits for the number to change
expect(received).toEqual(expected)
Expected value to equal:
105
Received:
100
9 | }, 600);
10 | await waitForExpect(() => {
> 11 | expect(numberToChange).toEqual(105);
12 | });
13 | });
14 |
at waitForExpect (src/waitForExpect.spec.js:11:28)
at waitUntil.catch (src/index.js:61:5)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 5.807s
You can add multiple expectations to wait for, all of them have to pass, and if one of them don't, it will be marked.
For example, let's add another expectation for a different number, notice how jest tells you that that's the expectation that failed.
expect(received).toEqual(expected)
Expected value to equal:
110
Received:
105
11 | await waitForExpect(() => {
12 | expect(numberToChange).toEqual(100);
> 13 | expect(numberThatWontChange).toEqual(110);
14 | });
15 | });
16 |
at waitForExpect (src/waitForExpect.spec.js:13:34)
at waitUntil.catch (src/index.js:61:5)
Since 0.6.0 we can now work with promises, for example, this is now possible:
test("rename todo by typing", async () => {
const todoToChange = getTodoByText("original todo");
todoToChange.value = "different text now";
Simulate.change(todoToChange);
await waitForExpect(() =>
expect(
todoItemsCollection.findOne({
text: "different text now"
})).resolves.not.toBeNull()
);
});
Async Await also works, as in this example - straight from our test case
test("it works with promises", async () => {
let numberToChange = 10;
const randomTimeout = Math.floor(Math.random() * 300);
setTimeout(() => {
numberToChange = 100;
}, randomTimeout);
const sleep = (ms) =>
new Promise(resolve => setTimeout(() => resolve(), ms));
await waitForExpect(async () => {
await sleep(10);
expect(numberToChange).toEqual(100);
});
});
(Note: Obviously, in this case it doesn't make sense to put the await sleep there, this is just for demonstration purpose)
API
waitForExpect takes 3 arguments, 2 optional.
Changelog
0.6.0 - 3 May 2018
Work with promises.
0.5.0 - 10 April 2018
Play nicely with jest fake timers (and also in any test tool that overwrites setTimeout) - thanks to @slightlytyler and @kentcoddods for helping to get this resolved.
Credit
Originally based on ideas from https://github.com/devlato/waitUntil.
Simplified highly and rewritten for 0.1.0 version.
Simplified even more and rewritten even more for 0.2.0 with guidance from Kent C. Dodds: https://github.com/kentcdodds/react-testing-library/pull/25