Управление временем с tick

Jasmine предоставляет мощные инструменты для тестирования асинхронного кода, где важную роль играет управление временем. Одним из таких инструментов является функция tick, которая позволяет симулировать прохождение времени в тестах, делая возможным проверку таймеров, задержек и промисов без реального ожидания.

Подготовка к использованию tick

Для работы с tick необходимо включить fake timers с помощью функции jasmine.clock().install(). Это подменяет стандартные таймеры JavaScript (setTimeout, setInterval) на управляемые Jasmine версии, что позволяет контролировать время точно и предсказуемо.

beforeEach(function() {
    jasmine.clock().install();
});

afterEach(function() {
    jasmine.clock().uninstall();
});

beforeEach и afterEach гарантируют, что каждый тест начнется с чистой среды, а любые изменения таймеров не будут влиять на другие тесты.

Основные возможности tick

Метод tick принимает один аргумент — количество миллисекунд, на которое необходимо “перемотать” время вперед. Все таймеры, назначенные на этот интервал, будут выполнены синхронно.

it("выполняет таймер через 100 мс", function() {
    const callback = jasmine.createSpy("callback");
    setTimeout(callback, 100);
    
    jasmine.clock().tick(100);
    
    expect(callback).toHaveBeenCalled();
});

Ключевые моменты:

  • Все таймеры до указанного времени вызываются синхронно.
  • Если вызвать tick меньше, чем длительность таймера, функция не сработает.
  • Возможна последовательная симуляция времени с накоплением: tick(50); tick(50); эквивалентно tick(100);.

Тестирование интервалов

Для функций, использующих setInterval, tick позволяет проверить многократные срабатывания интервала.

it("вызывает функцию несколько раз по интервалу", function() {
    const callback = jasmine.createSpy("callback");
    setInterval(callback, 200);

    jasmine.clock().tick(600);
    
    expect(callback.calls.count()).toBe(3);
});

Каждое срабатывание интервала учитывается, а количество вызовов можно проверить через calls.count().

Асинхронные операции с tick

Функции, использующие промисы и setTimeout, можно тестировать без реального ожидания. Важно понимать, что tick перематывает только таймеры, а промисы обрабатываются через микротаски. Для тестирования комбинаций таймеров и промисов необходимо использовать Promise.resolve() или async/await вместе с tick.

it("тестирует промис с таймером", async function() {
    let result = null;
    
    setTimeout(() => {
        result = "done";
    }, 100);

    jasmine.clock().tick(100);
    
    await Promise.resolve(); // дождаться микротасков
    
    expect(result).toBe("done");
});

Совместимость с setTimeout и setInterval

  • tick работает только с таймерами, созданными после вызова jasmine.clock().install().
  • Таймеры внутри сторонних библиотек, созданные до установки фейковых часов, не будут контролироваться.
  • Для корректного тестирования сторонних модулей, использующих глобальные таймеры, иногда требуется их явная перезагрузка после установки clock.

Особенности и подводные камни

  1. Накопление времени — каждый вызов tick добавляет указанное количество миллисекунд к внутреннему счетчику Jasmine. Это позволяет моделировать сложные сценарии с последовательными таймерами.
  2. Точные интервалы — метод не пропускает срабатывания таймеров, даже если время перемотано сразу на большое значение. Все таймеры срабатывают по порядку их назначения.
  3. Удаление часовjasmine.clock().uninstall() необходимо вызывать в afterEach, иначе последующие тесты могут работать некорректно.

Практические примеры

Пример 1. Таймер с задержкой и проверкой состояния:

it("изменяет состояние через 500 мс", function() {
    let state = "initial";

    setTimeout(() => {
        state = "updated";
    }, 500);

    expect(state).toBe("initial");
    jasmine.clock().tick(500);
    expect(state).toBe("updated");
});

Пример 2. Последовательные интервалы:

it("интервал с увеличением счетчика", function() {
    let counter = 0;
    setInterval(() => { counter += 2; }, 100);
    
    jasmine.clock().tick(300);
    expect(counter).toBe(6);
});

Использование tick позволяет тестировать сложные тайминговые сценарии полностью синхронно, исключая зависимость от реального времени и ускоряя выполнение тестового набора.