Jasmine предоставляет несколько способов тестирования асинхронного
кода: через колбэк done, через промисы и с использованием
async/await. Понимание того, как правильно комбинировать
эти подходы, критично для стабильности и читаемости тестов.
В Jasmine асинхронный тест обычно определяется одной из двух стратегий:
done: Этот
способ позволяет Jasmine понимать, когда асинхронная операция завершена.
Тест не будет считаться завершённым, пока не вызовется функция
done.it('должен дождаться завершения асинхронной функции через done', (done) => {
setTimeout(() => {
expect(true).toBe(true);
done();
}, 100);
});
async/await: Если функция теста объявлена как
async, Jasmine автоматически ждёт выполнения промиса,
возвращаемого этой функцией.it('должен дождаться завершения асинхронной функции через async/await', async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
async/await и doneНа первый взгляд может показаться, что объединение
async/await и done избыточно, но на практике
это полезно при необходимости точного контроля над временем завершения
теста или обработкой ошибок, которые могут возникнуть внутри
async функции и которые не всегда корректно ловятся Jasmine
без done.
it('должен корректно обрабатывать ошибки в async/await с done', (done) => {
(async () => {
try {
const result = await Promise.reject(new Error('Ошибка'));
// Этот код не будет выполнен
expect(result).toBeDefined();
done.fail('Ошибка не должна быть проброшена сюда');
} catch (err) {
expect(err.message).toBe('Ошибка');
done();
}
})();
});
Ключевые моменты:
done вызывается только один раз; повторный вызов
приведёт к ошибке Jasmine.async функции можно использовать
try/catch для обработки ошибок, а затем вызвать
done() или done.fail(error).done() после завершения асинхронной
операции, тест зависнет до таймаута.Асинхронные тесты часто требуют увеличения времени ожидания. Jasmine
по умолчанию использует 5 секунд. Таймаут можно изменить через третий
аргумент it:
it('должен дождаться медленной асинхронной функции', async (done) => {
await new Promise(resolve => setTimeout(resolve, 7000));
expect(true).toBe(true);
done();
}, 10000);
async/await без done. Это
упрощает код и делает тесты более читаемыми.done оправдано при работе с
callback-ориентированными библиотеками или когда требуется точное
управление завершением теста.async функций при
комбинировании с done всегда использовать
try/catch. Это предотвращает “зависание” теста при выбросе
исключения.Promise.all с async/await для параллельного
выполнения и затем вызывать done().it('должен дождаться нескольких асинхронных операций', (done) => {
(async () => {
try {
const results = await Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]);
expect(results).toEqual([1, 2, 3]);
done();
} catch (err) {
done.fail(err);
}
})();
});
done().async/await всегда использовать
try/catch или done.fail(error); иначе Jasmine
не сможет корректно зафиксировать падение теста.done и return промиса
без необходимости — это может вызвать непредсказуемое поведение.Комбинирование async/await и done
предоставляет полный контроль над асинхронными тестами, позволяя
одновременно использовать преимущества промисов и точного управления
завершением теста через колбэк. Это особенно важно при сложных сценариях
с множественными асинхронными зависимостями и обработкой ошибок.