Dev Ramble

Guides, how-to's and other nonsensical ramblings from a software engineer

Home

Mocking setTimeout With Fake Timers Using Jest

May 16, 2020

Timer functions like setTimeout are commonly used in JavaScript to schedule tasks to run after some delay. When writing unit tests to test this kind of functionality, it isn’t ideal to wait for real time to pass especially if the delays are hours long. Jest allows us to use “fake timers” to instantly run timers to completion, or even advance time by a specified amount.

What We Are Testing

In this example, we’ll be testing a method in a Vue component. However this method of mocking timers using Jest should be applicable to other frameworks as well.

In this Vue component, we have one method that creates an alert after 5 seconds. We’ll be writing a test case to test that this method does create the alert by mocking the timer to artificially advance it by 5 seconds.

<template>
    <div>
        <button @click="delayedAlert">Delayed alert</button>
    </div>
</template>

<script>
export default {
    name: "TimeoutExample",
    methods: {
        delayedAlert: function() {
            setTimeout(() => {
                alert("Hello!");
            }, 5000);
        }
    }
};
</script>
TimeoutExample.vue

Writing the Test

The first thing we need to do in our test is to let Jest know we need to use fake timers. This is done using the jest.useFakeTimers() method. Doing this will automagically replace setTimeout with mock functions.

To test that our callbacks are called after the timeout, you can use jest.runAllTimers to immediately run the callback methods.

Another option is to specify an amount of time to advance the timers by. This can be done using jest.advanceTimersByTime() as shown in the example below. Here, I’m advancing time by 5 seconds instantly which will trigger the alert callback.

import { shallowMount } from "@vue/test-utils";
import TimeoutExample from "@/components/TimeoutExample.vue";

describe("TimeoutExample.vue", () => {
    it("alerts after 5 seconds", () => {
        // Tell jest to use fake timers
        jest.useFakeTimers();

        // Spy on the window.alert() function
        const spy = jest.spyOn(window, 'alert').mockImplementation(() => { });

        // Mount the TimeoutExample component
        const wrapper = shallowMount(TimeoutExample);

        // Call the method we are testing
        wrapper.vm.delayedAlert();

        // Advance timers by 5 seconds
        jest.advanceTimersByTime(5000);

        // Check that alert has been called
        expect(spy).toBeCalledWith('Hello!');
    });
});
TimeoutExample.spec.js

Learn more about Timer Mocks and Jest here: https://jestjs.io/docs/en/timer-mocks

Share This Post