Timers

I begin from setting up the testing hooks to mock time in this test, using the vi.useFakeTimers() and vi.useRealTimers() functions, the same way I did in the previous exercise:
beforeAll(() => {
  vi.useFakeTimers()
})

afterAll(() => {
  vi.useRealTimers()
})
Note that mocking date and time includes mocking timers and intervals as well!
My next goal is to call the debouncedFn() and make sure that the wrapped fn function is not called since the debounce duration has not yet passed.
test('executes the callback after the debounce timeout passes', () => {
  const fn = vi.fn<(input: string) => void>()
  const debouncedFn = debounce(fn, 250)

  debouncedFn('one')
  expect(fn).not.toHaveBeenCalled()
Since the debounce() accepts any function as the argument, I am creating a mock function via vi.fn() so I can spy on its calls and assert them! Passing it a type argument of (input: number) => void also allows me to define the call signature of that mocked function to keep it type-safe in tests.
Now, let's shift the time!
Vitest provides us with a bunch of utility functions to control timers in test:
All of those are great tools to have in your toolbelt. In this test, I will use the vi.advanceTimersByTime() function to advance the time in test by 250 milliseconds.
vi.advanceTimersByTime(250)
This will immediately fastforward the test universe ahead by 250ms, allowing me to write my expectations toward the tested debounced function:
vi.advanceTimersByTime(250)

expect(fn).toHaveBeenCalledOnce()
expect(fn).toHaveBeenCalledWith('one')
The second test case will employ everything we've learned thus far to test the repetitive calls to the debounced function:
test('debounces the callback until the timeout passes since the last call', () => {
  const fn = vi.fn<(input: string) => void>()
  const debouncedFn = debounce(fn, 250)

  debouncedFn('one')
  expect(fn).not.toHaveBeenCalled()

  vi.advanceTimersByTime(100)

  debouncedFn('two')
  expect(fn).not.toHaveBeenCalled()

  vi.advanceTimersByTime(250)

  expect(fn).toHaveBeenCalledOnce()
  expect(fn).toHaveBeenCalledWith('two')
})
Note that you can use fake timers and vi.advanceTimersByTime() and vi.advanceTimersToNextTimer() to test intervals (setInterval) too!

Difference with sleep()

You may be wondering about the difference between vi.advanceTimersByTime() and something like a sleep() function. Since in this case we do need to wait for a fixed period of time to pass, using sleep() may seem like a viable alternative. But the two behave completely differently.
The vi.advanceTimersByTime() function advances the mocked time immediately. You can advance the timers by a year and only a millisecond will pass in your test (this is not an Interstellar reference). It allows you to be in full control over the time in your test, separating the mocked and the real time.
The sleep() function, on the other hand, will actually wait for the given time. So if your functionality has a timeout that exceeds your test's timeout, you won't be able to sleep it through. From this perspective, sleep() becomes a part of the action in your test, not the setup.
It's worth mentioning that since sleep() often depends on setTimeout(), it won't do anything if you are using fake timers anyway. You would have to advance the timers for the sleep() function as well for it to work.

Access Denied

You must login or register for the workshop to view the diff.

Check out this video to see how the diff tab works.