DEV Community

Christian Engel
Christian Engel

Posted on

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 want to mock
modules with jest and especially update the return values of module functions across my 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});
});
Enter fullscreen mode Exit fullscreen mode

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});
});
Enter fullscreen mode Exit fullscreen mode

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 actually 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});
});
Enter fullscreen mode Exit fullscreen mode

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});
});
Enter fullscreen mode Exit fullscreen mode

Pick whatever suits you the best. :)


This post was published on my personal blog, first.

You should follow me on dev.to for more tips like this. Click the follow button in the sidebar! 🚀

Discussion (0)