Тестування асинхронного коду
Асинхронний код є дуже поширеним в 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 (e) {
expect(e).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(e => expect(e).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)
.
done()
should not be mixed with promises as this tends to lead to memory leaks in your tests.
.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');
});
Жоден з наведених підходів не є кращим за інші і ви можете змішувати їх і поєднувати їх в своєму проекті або, навіть, в одному файлі. Це цілком залежить від того, який підхід робить тести простішими для написання для вас.