Асинхронные операции являются неотъемлемой частью современного
JavaScript-разработки. Методы работы с асинхронным кодом, такие как
setTimeout, setInterval, Promise,
async/await, используют временные задержки и внешние
ресурсы, с которыми необходимо взаимодействовать во время тестирования.
Для эффективного тестирования таких операций в Jest существуют
специальные инструменты — асинхронные матчеры.
Асинхронные матчеры в Jest позволяют тестировать функциональность, которая работает с асинхронным кодом, синхронизируя выполнение тестов с завершением асинхронных операций. Это помогает избежать ложных положительных или отрицательных результатов тестов, связанных с задержками выполнения.
resolvesМетод resolves используется для проверки, что промис был
успешно разрешён, и для проверки его значения. Он применяется в
сочетании с методами утверждения, такими как toBe,
toEqual и другими, чтобы удостовериться, что асинхронный
результат соответствует ожидаемому.
Пример использования:
test('Promise resolves to value', async () => {
await expect(Promise.resolve(42)).resolves.toBe(42);
});
В данном примере проверяется, что промис, возвращающий значение
42, разрешается с этим значением.
rejectsМетод rejects аналогичен resolves, но
используется для проверки отклонённых промисов. Он позволяет убедиться,
что промис был отклонён с ожидаемой ошибкой или значением.
Пример использования:
test('Promise rejects with error', async () => {
await expect(Promise.reject(new Error('Failed'))).rejects.toThrow('Failed');
});
Здесь проверяется, что промис отклоняется с ошибкой, содержащей сообщение ‘Failed’.
toBeResolved и
toBeRejectedЭто комбинации матчеров resolves и rejects,
которые предоставляют альтернативный, более краткий синтаксис для
проверок успешных или неудачных промисов.
Пример:
test('Promise resolves successfully', async () => {
await expect(Promise.resolve(10)).toBeResolved();
});
test('Promise rejects with an error', async () => {
await expect(Promise.reject(new Error('Something went wrong'))).toBeRejected();
});
Jest позволяет использовать async/await для
удобного тестирования асинхронных операций. С помощью await
можно “дожидаться” выполнения асинхронных операций в тестах, а
expect помогает осуществлять проверки на эти
результаты.
Пример использования:
test('Async function returns correct result', async () => {
const result = await asyncFunction();
expect(result).toBe('Success');
});
В этом примере асинхронная функция asyncFunction
вызывается с помощью await, и результат её выполнения
проверяется через expect.
setTimeout и
setIntervalАсинхронные операции, использующие setTimeout и
setInterval, могут вызвать проблемы в тестах, если не
учесть время, необходимое для их выполнения. Jest предоставляет методы,
такие как jest.useFakeTimers() и
jest.runAllTimers(), которые помогают манипулировать
временем в тестах.
Пример с использованием фальшивых таймеров:
jest.useFakeTimers();
test('setTimeout works correctly', () => {
const mockCallback = jest.fn();
setTimeout(() => {
mockCallback('Hello');
}, 1000);
expect(mockCallback).not.toHaveBeenCalled();
jest.runAllTimers(); // Мануальное "проведение" времени
expect(mockCallback).toHaveBeenCalledWith('Hello');
});
В данном тесте создаётся таймер с использованием
setTimeout, но с помощью фальшивых таймеров Jest управление
временем осуществляется вручную, что позволяет точно контролировать,
когда будет вызван mockCallback.
async функциями и таймерамиКогда тестируем асинхронные функции, использующие таймеры, важно
правильно синхронизировать ожидания. Использование
jest.advanceTimersByTime и
jest.runOnlyPendingTimers даёт возможность точно
имитировать и управлять таймерами.
Пример:
jest.useFakeTimers();
test('Async function with setTimeout behaves as expected', async () => {
const asyncFunctionWithTimer = () => new Promise((resolve) => {
setTimeout(() => {
resolve('done');
}, 500);
});
const resultPromise = asyncFunctionWithTimer();
jest.advanceTimersByTime(500); // Имитируем прохождение времени
await expect(resultPromise).resolves.toBe('done');
});
В этом примере асинхронная функция с таймером проверяется с помощью
jest.advanceTimersByTime, чтобы искусственно ускорить
выполнение теста и дождавшись разрешения промиса.
done в старых версиях JestВ старых версиях Jest для синхронизации асинхронных операций
использовался коллбек done. С помощью done
можно было явно указать, когда тест завершён, что позволяло избежать
проблем с таймингами.
Пример:
test('Async test using done callback', (done) => {
setTimeout(() => {
expect(true).toBe(true);
done();
}, 1000);
});
Однако в современных версиях Jest рекомендуется использовать
async/await вместо коллбека done, так как это
более современный и предпочтительный подход.
Когда тестируются промисы или асинхронные функции, важно помнить, что
ошибки могут быть не пойманы в случае, если они не будут явным образом
переданы в цепочку .catch() или использована конструкция
try/catch в асинхронных функциях.
Пример ловушки ошибки:
test('Promise rejects with an error', async () => {
await expect(Promise.reject(new Error('Error occurred'))).rejects.toThrow('Error occurred');
});
При таком подходе ошибка будет поймана, и тест завершится успешно, если она соответствует ожидаемому значению.
Асинхронные матчеры Jest — мощные инструменты для тестирования
асинхронных операций, таких как промисы, таймеры и асинхронные функции.
Они позволяют точно синхронизировать выполнение тестов и гарантировать
корректность работы асинхронного кода, исключая проблемы с временными
задержками и непредсказуемым поведением. Использование таких матчеров,
как resolves, rejects, а также возможность
управления временем через фальшивые таймеры, обеспечивает точность и
надёжность тестов в условиях асинхронной работы.