Головна небезпека в асинхронних тестах - тест завершується раніше, ніж перевірка. Тест, у якому жодна перевірка не виконалася, вважається пройденим.
Неправильно:
it('завантажує користувача', () => {
fetchUser(1).then((user) => {
expect(user.name).toBe('Олена'); // виконається після завершення тесту
});
});
Тест «зелений» навіть тоді, коли fetchUser повертає зовсім іншого користувача.
Правильно - async/await:
it('завантажує користувача', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('Олена');
});
Тестовий фреймворк чекає на Promise, який повертає функція тесту.
Перевірка відхиленого Promise:
it('кидає помилку для неіснуючого користувача', async () => {
await expect(fetchUser(999)).rejects.toThrow('Not found');
});
it('повертає дані', async () => {
await expect(fetchUser(1)).resolves.toMatchObject({ id: 1 });
});
await перед expect(...).rejects обов'язковий - без нього перевірка знову не встигне виконатися.
Варіант з try/catch потребує захисту:
it('кидає помилку', async () => {
expect.assertions(1); // тест упаде, якщо перевірок буде не рівно одна
try {
await fetchUser(999);
} catch (error) {
expect(error.message).toBe('Not found');
}
});
Без expect.assertions(1) тест пройде, якщо fetchUser помилково не кине винятку - блок catch просто не виконається.
Таймери й затримки не варто чекати по-справжньому (await new Promise(r => setTimeout(r, 3000))): тест стане повільним і нестабільним. Для цього є фейкові таймери.
Перевірка, що все дочекалося:
expect.hasAssertions()- хоча б одна перевірка виконалася;vi.waitFor(() => expect(...))- повторює перевірку, доки вона не пройде чи не мине час, коли результат з'являється не одразу.
Порада: напишіть тест, переконайтеся, що він падає, якщо зламати код. Тест, який ніколи не падав, міг нічого не перевіряти.