Tick с конкретным временем

В Jasmine tick используется вместе с функциями симуляции времени, такими как jasmine.clock(), для контроля асинхронных операций, основанных на таймерах (setTimeout, setInterval). Механизм позволяет тестам работать детерминированно, исключая реальные задержки и ускоряя выполнение тестов. Основное отличие tick с конкретным временем от простого tick() заключается в возможности симулировать прохождение точного интервала времени, что критично для проверки сложной логики с несколькими таймерами или последовательными задержками.

Подключение и установка clock

Перед использованием tick необходимо активировать подмену системного времени:

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

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

jasmine.clock().install() заменяет встроенные функции setTimeout, setInterval, clearTimeout и clearInterval собственными моками, позволяя контролировать их поведение. uninstall() возвращает исходное состояние системы.

Использование tick с конкретным временем

Метод jasmine.clock().tick(milliseconds) принимает число миллисекунд и симулирует их прохождение. Это позволяет “прокрутить” таймеры вперед без реальной задержки:

it("вызывает колбек через 1000 мс", function() {
    const callback = jasmine.createSpy("callback");
    setTimeout(callback, 1000);

    jasmine.clock().tick(500);
    expect(callback).not.toHaveBeenCalled();

    jasmine.clock().tick(500);
    expect(callback).toHaveBeenCalled();
});

Ключевой момент: tick симулирует именно прошедшее время, а не количество вызовов таймера. Если таймер установлен на 1000 мс, вызов tick(500) не приводит к его срабатыванию.

Прокрутка времени с несколькими таймерами

Когда в тесте несколько таймеров с разными интервалами, tick позволяет точно контролировать их последовательность:

it("правильно срабатывают несколько таймеров", function() {
    const first = jasmine.createSpy("first");
    const second = jasmine.createSpy("second");

    setTimeout(first, 300);
    setTimeout(second, 700);

    jasmine.clock().tick(300);
    expect(first).toHaveBeenCalled();
    expect(second).not.toHaveBeenCalled();

    jasmine.clock().tick(400);
    expect(second).toHaveBeenCalled();
});

Здесь важно, что суммарное время tick учитывается относительно установки таймеров, а не отдельно. Первый вызов tick(300) двигает “часы” на 300 мс, второй tick(400) — ещё на 400 мс, что в сумме даёт 700 мс.

Использование с setInterval

Метод tick применим и к интервалам. Таймер срабатывает столько раз, сколько должно было пройти за указанный интервал времени:

it("setInterval с tick", function() {
    const callback = jasmine.createSpy("callback");
    setInterval(callback, 200);

    jasmine.clock().tick(200);
    expect(callback.calls.count()).toBe(1);

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

Особенность: интервал срабатывает каждый раз, когда “время движется” на шаг больше или равный его интервалу, даже если tick вызывается сразу на несколько интервалов.

Ограничения и нюансы

  1. Асинхронные промисы: jasmine.clock().tick() не влияет на промисы или другие микротаски, основанные на Event Loop. Для работы с ними необходимо использовать async/await или done() вместе с реальными таймерами.
  2. Однократная установка clock: важно устанавливать и убирать clock() в beforeEach/afterEach, иначе мокирование может повлиять на соседние тесты.
  3. Суммарное время: tick всегда суммирует прошедшее время, поэтому последовательные вызовы учитывают предыдущие шаги.

Пример сложной последовательности

it("комбинация setTimeout и setInterval", function() {
    const single = jasmine.createSpy("single");
    const repeat = jasmine.createSpy("repeat");

    setTimeout(single, 500);
    setInterval(repeat, 300);

    jasmine.clock().tick(300);
    expect(single).not.toHaveBeenCalled();
    expect(repeat.calls.count()).toBe(1);

    jasmine.clock().tick(200);
    expect(single).toHaveBeenCalled();
    expect(repeat.calls.count()).toBe(1);

    jasmine.clock().tick(300);
    expect(repeat.calls.count()).toBe(2);
});

В этом примере tick позволяет синхронизировать срабатывания разных таймеров без реальных задержек, проверяя как одиночные таймауты, так и повторяющиеся интервалы.

Практические советы

  • Использовать конкретные значения миллисекунд в tick, чтобы тесты были предсказуемыми.
  • Не использовать tick() без аргумента — это может привести к неконтролируемым срабатываниям.
  • Для сложных сценариев с несколькими таймерами лучше разбивать tick на последовательные шаги, чтобы точно отслеживать состояние.

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