Testing Asynchronous Code
Асинхронний код є дуже поширеним в JavaScript. Коли у вас є код, який працює асинхронно, Jest повинен знати, коли код, що тестується, закінчив свою роботу перед тим, як перейти до наступного тесту. Jest дозволяє це зробити кількома способами.
Зворотні виклики #
Найбільш популярним асинхронним паттерном є зворотні виклики.
Наприклад, нехай у вас є функція fetchData(callback)
, яка отримує дані і викликає callback(data)
, коли дані отримано. Ви хочете перевірити, що отримані дані - це рядок "peanut butter"
.
За замовчуванням тести в Jest завершуються, коли доходять до кінця свого виконаня. Це значить, що наступний тест не буде працювати, як того хотілося б:
// Не робіть цього! test('the data is peanut butter', () => { function callback(data) { expect(data).toBe('peanut butter'); } fetchData(callback); });
Проблема в тому, що тест закінчується як тільки виконається код функції fetchData
, до того, як буде виконано зворотній виклик.
Існує альтернативний вигляд тесту
, який це виправляє. Замість того, щоб писати тест в функції без аргументів, використайте аргумент done
. В такому випадку Jest чекатиме виконання зворотнього виклику done
перед тим, як завершити тест.
test('the data is peanut butter', done => { function callback(data) { expect(data).toBe('peanut butter'); done(); } fetchData(callback); });
Якщо done()
не буде викликано, то тест буде провалено, що є саме тим, що вам потрібно.
Проміси #
Якщо ваш код використовує проміси, то є простіший спосіб писати асинхронні тести. Просто поверніть проміс зі свого тесту і Jest чекатиме поки проміс буде виконано. Якщо проміс буде відхилений, тест автоматично впаде.
Наприклад, уявімо, що fetchData
, замість використання зворотніх викликів повертає проміс, який повинен бути вирішений з рядком "peanut butter"
. Ми можемо протестувати це таким чином:
test('the data is peanut butter', () => { expect.assertions(1); return fetchData().then(data => { expect(data).toBe('peanut butter'); }); });
Завжди переконуйтеся, що ви повертаєте проміс. Якщо ви пропустите оператор return
, ваш тест закінчиться до того, як fetchData
виконається.
Якщо ви очікуєте, що проміс буде відхилено, використовуйте метод .catch
. Не забудьте додати expect.assertions
щоб переконатися, що певна кількість перевірок була виконана. В іншому випадку виконаний проміс не викличе помилку теста.
test('the fetch fails with an error', () => { expect.assertions(1); return fetchData().catch(e => expect(e).toMatch('error') ); });
.resolves
/ .rejects
#
Доступно в Jest 20.0.0+ #
Ви також можете використати матчер .resolves
у ваших конструкціях expect. Тоді Jest чекатиме поки проміс буде виконано. Якщо проміс буде відхилено, тест автоматично закінчиться невдачею.
test('the data is peanut butter', () => { expect.assertions(1); return expect(fetchData()).resolves.toBe('peanut butter'); });
Завжди переконуйтеся, що ви повертаєте твердження. Якщо ви пропустите оператор return
, ваш тест закінчиться до того, як fetchData
виконається.
Якщо ви очікуєте, що проміс буде відхилено, використовуйте матчер .rejects
. Він працює аналогічно матчеру .resolves
. Якщо проміс буде виконано успішно, це викличе помилку в тесті.
test('the fetch fails with an error', () => { expect.assertions(1); return expect(fetchData()).rejects.toMatch('error'); });
Async/Await #
Окрім того ви можете використовувати async
та await
у ваших тестах. Щоб написати асинхроний тест, додайте ключове слово async
перед функцією, яку ви передаєте в test
. Наприкоад, та сама функція fetchData
може бути протестована так:
test('the data is peanut butter', async () => { expect.assertions(1); 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
(доступні в Jest 20.0.0+).
test('the data is peanut butter', async () => { expect.assertions(1); await expect(fetchData()).resolves.toBe('peanut butter'); }); test('the fetch fails with an error', async () => { expect.assertions(1); await expect(fetchData()).rejects.toMatch('error'); });
В цих випадках async
і await
виступають як синтаксичний цукор для тієї ж логіки, що використовують приклади з промісами.
Жоден з наведених підходів не є кращим за інші і ви можете змішувати їх і поєднувати їх в своєму проекті або, навіть, в одному файлі. Це цілком залежить від того, який підхід робить ваші тести простішими.