Hone logo
Hone
Problems

Master Jest's advanceTimersByTime for Precise Timer Control

Jest's advanceTimersByTime is a powerful tool for testing code that relies on timers like setTimeout and setInterval. This challenge will test your ability to effectively use this function to simulate the passage of time and verify timer-based behavior in your TypeScript code. Mastering this will allow you to write robust and reliable tests for asynchronous operations.

Problem Description

You are tasked with testing a TypeScript function that uses setTimeout to perform an action after a specified delay. You need to write Jest tests that use jest.useFakeTimers() and jest.advanceTimersByTime() to advance the fake timers and assert that the function executes as expected at the correct times.

Key Requirements:

  1. Enable Fake Timers: Your tests must enable Jest's fake timers.
  2. Advance Timers: Use jest.advanceTimersByTime(ms) to simulate the passage of time.
  3. Test setTimeout: Verify that a function called with setTimeout executes after the specified delay.
  4. Test Multiple Timers: Handle scenarios where multiple setTimeout calls are made, ensuring each executes at its designated time.
  5. Test Immediate Execution (0ms delay): Ensure timers with a 0ms delay are handled correctly.

Expected Behavior:

The function under test should execute its callback only after the specified delay has passed, as simulated by advanceTimersByTime.

Edge Cases:

  • Testing with setTimeout that has a delay of 0ms.
  • Testing with multiple setTimeout calls with different delays.

Examples

Example 1: Basic setTimeout Test

Function under test:

function greetAfterDelay(name: string, delay: number, callback: (message: string) => void): void {
  setTimeout(() => {
    callback(`Hello, ${name}!`);
  }, delay);
}

Input to test:

name = "Alice", delay = 1000

Test Scenario:

Call greetAfterDelay and then advance timers by 1000ms. Assert that the callback is called with "Hello, Alice!".

Output:

The callback should be invoked with the message "Hello, Alice!".

Explanation:

We enable fake timers, call greetAfterDelay, and then advance the timers by 1000ms. This simulates the passage of 1 second, triggering the setTimeout callback. We then assert that the callback was indeed called with the expected message.

Example 2: Testing Multiple setTimeout Calls

Function under test:

function performSequencedActions(actions: { name: string, delay: number, callback: (name: string) => void }[]): void {
  actions.forEach(action => {
    setTimeout(() => {
      action.callback(action.name);
    }, action.delay);
  });
}

Input to test:

An array of actions:

  • { name: "first", delay: 500, callback: (name) => console.log(name) }
  • { name: "second", delay: 1200, callback: (name) => console.log(name) }
  • { name: "third", delay: 200, callback: (name) => console.log(name) }

Test Scenario:

Call performSequencedActions with the above array. Advance timers incrementally and check when each action's callback is invoked.

Expected Order of Callback Invocations:

  1. "third" (after 200ms)
  2. "first" (after 500ms)
  3. "second" (after 1200ms)

Explanation:

We need to assert that the callbacks are executed in the correct order based on their delays. This requires careful advancement of timers and checking the mock function calls.

Example 3: setTimeout with 0ms Delay

Function under test:

function processImmediately(callback: () => void): void {
  setTimeout(callback, 0);
}

Input to test:

A mock callback function.

Test Scenario:

Call processImmediately with the mock callback. Advance timers by 0ms (or simply proceed to the next assertion after enabling timers) and assert that the callback has been invoked.

Output:

The mock callback should be invoked.

Explanation:

setTimeout with a 0ms delay schedules the callback to run in the next event loop tick. When using fake timers, advancing by 0ms or proceeding after enabling timers should trigger this.

Constraints

  • All timer delays will be non-negative integers.
  • The function under test will only use setTimeout.
  • You will be provided with the function to test and will need to write the Jest tests in TypeScript.

Notes

  • Remember to use jest.useFakeTimers() to enable Jest's timer mocking.
  • Use jest.advanceTimersByTime(ms) to simulate the passage of time.
  • Consider using jest.fn() to create mock callback functions to assert on their invocation and arguments.
  • jest.runOnlyPendingTimers() can be useful in some scenarios, but advanceTimersByTime is the primary tool for this challenge.
  • Ensure you clean up your fake timers after each test using jest.useRealTimers() or Jest's automatic cleanup.
Loading editor...
typescript