В JavaScript функции setTimeout и
setInterval используются для выполнения кода с задержкой
или циклически через определённые интервалы времени. При тестировании с
помощью Jasmine важно учитывать асинхронность этих вызовов, чтобы тесты
оставались предсказуемыми и надежными.
Проблема асинхронности: простое использование
setTimeout в тесте без обработки асинхронного поведения
приведет к тому, что Jasmine завершит тест до выполнения задержки. Это
вызывает ложные успехи или ошибки. Для корректного тестирования
необходимо либо использовать асинхронные колбэки Jasmine
(done), либо подменять таймеры на управляемые “фейковые
таймеры” (jasmine.clock()).
donedescribe('Асинхронный тест с setTimeout', function() {
it('должен выполнить функцию через 100 мс', function(done) {
let value = false;
setTimeout(function() {
value = true;
expect(value).toBe(true);
done();
}, 100);
});
});
Особенности:
done передаётся Jasmine как аргумент
теста.expect вызывается внутри таймера.done тест завершится до выполнения
setTimeout, что приведет к ошибке.Jasmine предоставляет возможность подменять стандартные таймеры с
помощью jasmine.clock(). Это особенно удобно для ускорения
тестов и точного контроля времени.
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
Обязательные шаги: установка перед каждым тестом и восстановление после теста для предотвращения конфликтов с другими тестами.
setTimeout с jasmine.clock()describe('Тестирование setTimeout с фейковым таймером', function() {
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it('должен изменить значение через 100 мс', function() {
let value = false;
setTimeout(function() {
value = true;
}, 100);
expect(value).toBe(false); // До прогонки таймера
jasmine.clock().tick(100); // Прогоняем время на 100 мс
expect(value).toBe(true); // После прогонки таймера
});
});
Пояснения:
jasmine.clock().tick(ms) перематывает
“виртуальное время” на заданное количество миллисекунд.tick.setInterval с фейковыми таймерамиsetInterval вызывает функцию многократно через заданный
интервал. Для тестирования нужно симулировать прогон времени
соответствующее количество раз.
describe('Тестирование setInterval', function() {
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it('должен вызывать функцию каждые 50 мс', function() {
let counter = 0;
setInterval(function() {
counter++;
}, 50);
expect(counter).toBe(0);
jasmine.clock().tick(50);
expect(counter).toBe(1);
jasmine.clock().tick(150);
expect(counter).toBe(4); // 50 + 150 = 200 мс, 4 вызова
});
});
Особенности:
tick() учитывает все интервалы, прошедшие за указанное
время.jasmine.clock()Подмена Date:
jasmine.clock() позволяет управлять системным временем с
помощью mockDate. Это удобно при тестировании кода,
зависящего от текущей даты и времени.
const baseDate = new Date(2026, 0, 1);
jasmine.clock().mockDate(baseDate);
expect(new Date().getTime()).toBe(baseDate.getTime());Комбинирование с setTimeout и
setInterval: Можно одновременно тестировать и
задержки, и циклические вызовы, контролируя порядок событий.
done или tick:
приводит к ложным результатам.jasmine.clock(): может
влиять на другие тесты.setInterval: важно учитывать, что метод
tick суммирует все интервалы, а не только один вызов.done для единичных, простых таймеров,
когда тест не требует ускорения времени.jasmine.clock() для ускорения тестов,
работы с множественными таймерами и детерминированного поведения.beforeEach и afterEach.Точное управление временем и таймерами позволяет создавать быстрые, предсказуемые и надежные тесты асинхронного кода в Jasmine, что является ключевым элементом профессиональной разработки на JavaScript.