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