Перейти до основного змісту
Версія: Next

Тестування асинхронного коду

Асинхронний код є дуже поширеним в 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 виступають як синтаксичний цукор для тієї ж логіки, що використовують приклади з промісами.

caution

Завжди переконуйтеся, що ви повертаєте проміс (або використовуєте 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).

caution

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');
});

Жоден з наведених підходів не є кращим за інші і ви можете змішувати їх і поєднувати їх в своєму проекті або, навіть, в одному файлі. Це цілком залежить від того, який підхід робить тести простішими для написання для вас.