Тестирование асинхронного кода
В JavaScript часто встречается асинхронное выполнение кода. Если ваш код выполняется асинхронно, Jest должен знать, когда тестируемый код завершил работу, прежде чем перейти к следующему тесту. Jest предлагает несколько способов обработки таких случаев.
Промисы
Верните из теста промис, и Jest дождётся его выполнения. Если промис будет отклонён, тест завершится с ошибкой.
Например, допустим, что fetchData возвращает промис, который должен выполниться со строкой 'peanut butter'. Это можно проверить так:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
Async/Await
В качестве альтернативы в тестах можно использовать async и await. Чтобы написать асинхронный тест, добавьте ключевое слово async перед функцией, передаваемой в test. Например, тот же сценарий с fetchData можно проверить так:
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');
}
});
Можно сочетать async и await с .resolves или .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');
});
В этих случаях async и await фактически являются синтаксическим сахаром для той же логики, что и в примере с промисами.
Обязательно верните промис (или используйте await). Если опустить инструкцию return/await, тест завершится до того, как промис, возвращённый из fetchData, выполнится или будет отклонён.
Если вы ожидаете, что промис будет отклонён, используйте метод .catch. Не забудьте добавить expect.assertions, чтобы проверить, что вызвано определённое количество утверждений. Иначе выполненный промис не приведёт к сбою теста.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});
Обратные вызовы
Если вы не используете промисы, можно использовать обратные вызовы. Например, допустим, что fetchData вместо возврата промиса ожидает обратный вызов, то есть получает некоторые данные и вызывает callback(null, data) по завершении. Вы хотите проверить, что возвращённые данные — это строка 'peanut butter'.
По умолчанию выполнение тестов Jest завершается, как только они доходят до конца. Это означает, что этот тест не будет работать так, как задумано:
// 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);
});
Проблема в том, что тест завершится сразу после завершения fetchData, так и не вызвав обратный вызов.
Для test существует альтернативная форма, которая решает эту проблему. Вместо того чтобы помещать тест в функцию без аргументов, используйте один аргумент с именем done. Jest будет ждать вызова обратного вызова done, прежде чем завершить тест.
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);
});
Если done() так и не будет вызван, тест завершится с ошибкой (по тайм-ауту), чего и следует ожидать.
Если инструкция expect завершится ошибкой, она выбросит исключение, и done() не будет вызван. Если мы хотим видеть в журнале теста причину сбоя, нужно обернуть expect в блок try и передать ошибку из блока catch в done. Иначе мы получим неинформативную ошибку по тайм-ауту, в которой не будет указано, какое значение получил expect(data).
Jest выбросит ошибку, если одной и той же функции теста передать обратный вызов done() и вернуть промис. Это сделано для предотвращения утечек памяти в тестах.
.resolves / .rejects
В выражении expect также можно использовать сопоставитель .resolves, и Jest дождётся выполнения этого промиса. Если промис будет отклонён, тест автоматически завершится с ошибкой.
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
Обязательно верните результат проверки: если опустить инструкцию return, тест завершится до того, как промис, возвращённый из fetchData, будет выполнен и then() сможет вызвать обратный вызов.
Если вы ожидаете, что промис будет отклонён, используйте сопоставитель .rejects. Он работает аналогично сопоставителю .resolves. Если промис будет выполнен, тест автоматически завершится с ошибкой.
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Ни один из этих вариантов не имеет особых преимуществ перед остальными. Их можно комбинировать в разных частях кодовой базы и даже в одном файле. Выбирайте тот стиль, который упрощает ваши тесты.
© 2022 Facebook, Inc.
Licensed under the MIT License.
https://jestjs.io/docs/asynchronous