Jest: тестирование асинхронного кода
Jest поддерживает несколько способов тестирования асинхронного кода:
async/await,.resolves/.rejectsматчеры, callback сdone, и фиктивные таймеры для setTimeout/setInterval.
Зачем нужно
Асинхронный код — основа современного JS: fetch-запросы, работа с БД, таймеры, очереди событий. Если не обработать асинхронность корректно, Jest завершит тест до разрешения Promise и не поймает ошибки. Знание паттернов async-тестирования обязательно для любого frontend и backend разработчика.
Где используется
- Тестирование API-сервисов и HTTP-клиентов
- Тестирование Redux thunk, saga, React Query
- Проверка поведения с задержками (debounce, retry)
- Тестирование WebSocket, потоков, event emitter
Основной контент
async/await — основной подход
test('получает данные пользователя', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('Alice');
});
test('выбрасывает ошибку при 404', async () => {
await expect(fetchUser(999)).rejects.toThrow('Not found');
});
.resolves / .rejects матчеры
test('Promise разрешается с нужным значением', () => {
return expect(fetchUser(1)).resolves.toEqual({ id: 1, name: 'Alice' });
});
test('Promise отклоняется', () => {
return expect(fetchUser(0)).rejects.toThrow('Invalid id');
});
// Важно: return обязателен — без него тест пройдёт не дождавшись Promise
Callback с done
// Старый стиль — предпочитай async/await
test('callback вызывается с данными', (done) => {
fetchUserCb(1, (err, user) => {
expect(err).toBeNull();
expect(user.name).toBe('Alice');
done; // сообщает Jest что тест завершён
});
});
Фиктивные таймеры
jest.useFakeTimers;
test('вызывает callback через 1 секунду', () => {
const callback = jest.fn;
setTimeout(callback, 1000);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
});
afterEach(() => {
jest.useRealTimers; // восстанавливаем реальные таймеры
});
Гарантия выполнения assertions
test('обрабатывает успешный ответ', async () => {
expect.assertions(2); // ожидаем ровно 2 вызова expect
try {
const data = await fetchUser(1);
expect(data.id).toBe(1);
expect(data.name).toBe('Alice');
} catch (e) {
// если попадаем сюда — assertions не выполнятся и тест упадёт
}
});
Частые ошибки
- Нет
awaitилиreturn— тест завершается до разрешения Promise и проходит даже если Promise отклонился с ошибкой - Нет
doneв callback-тесте — Jest завершает тест немедленно, не дожидаясь callback - Реальные таймеры в тестах — тест с
setTimeout(fn, 5000)занимает 5 секунд; используйjest.useFakeTimers - Не перехвачен отклонённый Promise — без
rejects.toThrowилиtry/catchошибка теряется