Тестування асинхронного коду
Асинхронний код є дуже поширеним в JavaScript. Коли у вас є код, який працює асинхронно, Jest повинен знати, коли код, що тестується, закінчив свою роботу перед тим, як перейти до наступного тесту. Jest дозволяє це зробити кількома способами.
Проміси
Просто поверніть проміс з вашого тесту і Jest чекатиме, поки він виконається. Якщо проміс буде відхилений, то тест впаде.
For example, let's say that fetchData returns a promise that is supposed to resolve to the string 'peanut butter'. Ми можемо протестувати це так:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
Async/Await
Alternatively, you can use async and await in your tests. To write an async test, use the async keyword in front of the function passed to test. For example, the same fetchData scenario can be tested with:
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (error) {
expect(error).toMatch('error');
}
});
You can combine async and await with .resolves or .rejects.
test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});
In these cases, async and await are effectively syntactic sugar for the same logic as the promises example uses.
Be sure to return (or await) the promise - if you omit the return/await statement, your test will complete before the promise returned from fetchData resolves or rejects.
If you expect a promise to be rejected, use the .catch method. Make sure to add expect.assertions to verify that a certain number of assertions are called. В іншому випадку, виконаний проміс не викличе помилку теста.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});
Зворотні в иклики
Якщо ви не використовуєте проміси, то ви можете скористатися колбеками. For example, let's say that fetchData, instead of returning a promise, expects a callback, i.e. fetches some data and calls callback(null, data) when it is complete. You want to test that this returned data is the string 'peanut butter'.
За замовчуванням тести в Jest завершуються, коли досягають кінця виконання. That means this test will not work as intended:
// Don't do this!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
The problem is that the test will complete as soon as fetchData completes, before ever calling the callback.
There is an alternate form of test that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called done. Jest will wait until the done callback is called before finishing the test.
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
If done() is never called, the test will fail (with timeout error), which is what you want to happen.
If the expect statement fails, it throws an error and done() is not called. If we want to see in the test log why it failed, we have to wrap expect in a try block and pass the error in the catch block to done. Otherwise, we end up with an opaque timeout error that doesn't show what value was received by expect(data).
Jest will throw an error, if the same test function is passed a done() callback and returns a promise. Це робиться для запобігання витоку пам'яті у ваших тестах.
.resolves / .rejects
You can also use the .resolves matcher in your expect statement, and Jest will wait for that promise to resolve. Якщо проміс буде відхилений, тест автоматично впаде.
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
Be sure to return the assertion—if you omit this return statement, your test will complete before the promise returned from fetchData is resolved and then() has a chance to execute the callback.
If you expect a promise to be rejected, use the .rejects matcher. It works analogically to the .resolves matcher. Якщо проміс буде виконано успішно, це викличе помилку в тесті.
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Жоден з наведених підходів не є кращим за інші і ви можете змішувати їх і поєднувати їх в своєму проекті або, навіть, в одному файлі. Це цілком залежить від того, який підхід робить тести простішими для написання для вас.