web://❤ A web related blog by Christian Engel

Mocking modules with jest and typescript

Since it took me some time to understand the behaviour and the jest docs don't tell much about it, I wanted to write down how to mock modules with jest and especially update the return values of module functions across your tests.

Consider you want to test a function called someFunctionToTest from a module called moduleA:

import {someFunctionToTest} from "moduleA";

test("It does something", () => {
    expect(someFunctionToTest()).toEqual({something: true});
});

Now you notice that the function relies on calling the method helper from moduleB. You want to mock that function and simulate a return value:

Variant 1

import {someFunctionToTest} from "moduleA";
import {helper} from "moduleB";

jest.mock("moduleB");

test("It does something", () => {
    (helper as jest.Mock).mockReturnValue(true);
    expect(someFunctionToTest()).toEqual({something: true});
});

test("It does something else", () => {
    (helper as jest.Mock).mockReturnValue(false);
    expect(someFunctionToTest()).toEqual({something: false});
});

The trick - and its a bit counter-intuitive - is to import the mocked function as well. See line 2 where we import the helper method into our test file. This is acutally the mock function. The reason is that jest does some code shifting and altough the call to jest.mock occures later, it will be executed before any imports are made. Therefore, helper is an instance of jest.fn() in our test file.

Now we can update the return values of our mocked function by calling mockedFunction.mockReturnValue(). There is only one catch: typescript will complain that the imported helper has no function mockReturnValue to be called. Thats because typescript does not know that helper is not the real thing but a mocked function. We can get around it by casting the type locally to jest.Mock.

Variant 2

After working some time with the variant 1, I came up with a variant 2 that may be more effective when you write lots of tests:

import {someFunctionToTest} from "moduleA";
import {helper as helperOriginal} from "moduleB";
const helper = helperOriginal as jest.Mock;

jest.mock("moduleB");

test("It does something", () => {
    helper.mockReturnValue(true);
    expect(someFunctionToTest()).toEqual({something: true});
});

test("It does something else", () => {
    helper.mockReturnValue(false);
    expect(someFunctionToTest()).toEqual({something: false});
});

This way, the helper method is not directly imported but imported as an alternatively named variable and then re-assigned and casted to type jest.Mock.

There is also a third variant (if your linter allows you to use require()):

Variant 3

import {someFunctionToTest} from "moduleA";
const { helper } = require("moduleB") as { helper: jest.Mock };

jest.mock("moduleB");

test("It does something", () => {
    helper.mockReturnValue(true);
    expect(someFunctionToTest()).toEqual({something: true});
});

test("It does something else", () => {
    helper.mockReturnValue(false);
    expect(someFunctionToTest()).toEqual({something: false});
});

Pick whatever suits you the best. :)