Тестування асинхронного коду
Асинхронний код є дуже поширеним в 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 завершуються, коли досягають кінця виконання. Це означає, що даний тест не працює, як задумано:
// Не робіть так!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
Проблема в тому, що тест закінчується як тільки виконається код функції fetchData
, до того, як буде викликано функцію зворотнього виклику.
Існує альтернативний вигляд тесту
, який це виправляє. Замість того, щоб писати тест в функції без аргументів, використайте аргумент 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
і передати помилку у виклик done
всередині блоку catch
. В іншому випадку тест завершиться з неявною помилкую таймауту, яка не покаже, яке значення було отримане в expect(data)
.
Jest викине помилку, якщо така ж сама тестова функція проходить зворотний виклик done()
і повертає проміс. Це робиться для запобігання витоку пам'яті у ваших тестах.
.resolves
/ .rejects
Ви також можете використати матчер .resolves
у ваших конструкціях expect. Тоді 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');
});
Жоден з наведених підходів не є кращим за інші і ви можете змішувати їх і поєднувати їх в своєму проекті або, навіть, в одному файлі. Це цілком залежить від того, який підхід робить тести простішими для написання для вас.