Skip to content
Theme:

Mocking functions and modules with Jest

A JavaScript program can have several dependencies, internal and/or external, most likely represented by the list of imports on the very top of your script. To write deterministic, repeatable unit tests, we need to control the inputs, outputs and invocations of those dependencies. These objects that simulate the real objects are mocks. Let’s have a look at Wikipedia definition of Mock object.

Mock objects are simulated objects that mimic the behavior of real objects in controlled ways, most often as part of a software testing initiative.

There is plenty of JavaScript mocking libraries out there. Today I am going to review a few methods of creating functions and modules mock using my favourite testing framework, Jest.

To mock or not to mock?

Mocking all dependencies feels like a perfect solution. Who doesn’t like to have total control? Unfortunately it may cause many false positives, because we end up testing a particular scenario implemented in a particular way. It is not a reliable test of a produced result.

On the other hand, why should we use mocks at all? Won’t we get the most accurate results by testing software as it is implemented? Yes — but this is out of scope of a unit test. Unit test should be isolated, narrow in scope and quick to execute.

“Please don’t mock me” by Justin Searls on YouTube is a fantastic talk about things that should be mocked and where mocking should be avoided. “Mock Objects: Shortcomings and Use Cases” by Alex Ruiz is another resource that I found very helpful. If you have to mock too much then this may indicate a high degree of coupling in your application. “Mocking is a Code Smell” by Eric Elliott is a very helpful article that can help you to reduce tight coupling and a requirement of widespread mocking.

Mocking is required when our supposed atomic units of composition are not really atomic, and our decomposition strategy has failed to decompose the larger problem into smaller, independent problems.

These are my typical candidates for mocks:

  • mock API calls
  • mock databases queries
  • mock conditions difficult to generate in a test environment
Picture of website with helpful resources to understand mocks

Jest mocks

The Jest testing framework comes with great mocking methods built-in for functions as well as modules. Let’s have a look at them all.

Function mock using jest.fn()

The simplest and most common way of creating a mock is jest.fn() method. If no implementation is provided, it will return the undefined value. There is plenty of helpful methods on returned Jest mock to control its input, output and implementation. Let’s have a look at a few examples.

it("returns undefined and has been called correct number of times", () => {
  const mock = jest.fn();

  const result = mock();

  expect(result).toBeUndefined();
  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith();
});

it("has been called with correct arguments and returns a correct value", () => {
  const mock = jest
    .fn()
    .mockReturnValueOnce("first return")
    .mockReturnValueOnce("second return");

  const resultFirst = mock("first call");
  const resultSecond = mock("second call");

  expect(resultFirst).toBe("first return");
  expect(resultSecond).toBe("second return");
  expect(mock).toHaveBeenCalledTimes(2);
  expect(mock).toHaveBeenNthCalledWith(1, "first call");
  expect(mock).toHaveBeenNthCalledWith(2, "second call");
});

Function mock using jest.spyOn()

Another method of creating a function mock is a jest.spyOn() method. Same like jest.fn() it creates a controlled mock. The key difference is the fact that by default it calls the original implementation. It stores in memory the original implementation so in case it has been redefined, jest.spyOn() allows us to restore the initial implementation using mockRestore() method. This is not something that we can do using jest.fn(). Example:

// helpers.js
module.exports = {
  add: (a, b) => a + b,
};
const helpers = require("./helpers");

it("returns correct result", () => {
  const addMock = jest.spyOn(helpers, "add");

  const result = addMock(1, 2);

  // look, in contrast to jest.fn() that returns undefined by default
  // jest.spyOn() calls original implementation
  expect(result).toBe(3);
});

it("returns mocked and original result", () => {
  const addMock = jest.spyOn(helpers, "add");
  addMock.mockImplementation(() => 4);

  // redefined implementation
  expect(helpers.add()).toBe(4);

  addMock.mockRestore();

  // original implementation
  expect(helpers.add(1, 2)).toBe(3);
});

I’m sorry for the terrible example above, this is because in real life you have no valid reasons to mock pure functions like add(). This is purely for illustrative purpose.

Module mock using jest.mock()

To entirely mock a module we can do something like this…

// helpers.js
module.exports = {
  add: (a, b) => a + b,
  sub: (a, b) => a - b,
};
const helpers = require("./helpers");

it("mocks entire module", () => {
  helpers.add = jest.fn();
  helpers.sub = jest.fn();

  expect(helpers.add.mock).toBeTruthy();
  expect(helpers.sub.mock).toBeTruthy();
});

It works, but what if a module exports tens or hundreds of methods? Manually reassigning all of them would be cumbersome. Jest comes with a fantastic feature called automock that you can enable globally or inside individual test files using jest.mock() method.

const helpers = require("./helpers");

jest.mock("./helpers");

it("mocks entire module", () => {
  expect(helpers.add.mock).toBeTruthy();
  expect(helpers.sub.mock).toBeTruthy();
});

Much nicer, isn’t it? Internally Jest inspects the module and creates a mock that conforms to original’s exports. Pretty neat!

Hot tip — name your mock

Look at this test and its result.

it("calls a function", () => {
  const mock = jest.fn();

  expect(mock).toHaveBeenCalledTimes(1);
});
expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0
Picture of simple jest usage of mock without descriptive name

This is OK if we have one test in a file, but it is hard to guess what jest.fn() is in a hundred lines long file. There is a simple solution — give your mock a descriptive name using mockName() method. Look!

it("calls a function", () => {
  const mock = jest.fn().mockName("my dope mock");

  expect(mock).toHaveBeenCalledTimes(1);
});
expect(my dope mock).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0
Picture of simple jest usage of mock with descriptive name

Hopefully you found this article helpful and you learned a thing or two. Until next time, stay curious!

Comments

  • d
    devmentor.pl

    Nice! Thx for your post!

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Ooo, thanks! I am glad it helped you out!
      Have a lovely day 🥑

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • V
    Vikash Sharma

    thanks for the article. wanted to mock module with parameters , say i have add module and it return 3 when i pass value 1 and 2 and 4 when i pass 2 and 2. how this can be mocked ?

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      I wouldn't advice to mock pure functions like `sum`. Just hit an actual implementation to test it.

      If you use `sum` just as an example, you need to look into something called `jest-when`.


      import { when } from 'jest-when';

      const fn = jest.fn()
      when(fn)
      .calledWith(1, 2).mockReturnValue(3)
      .calledWith(2, 2).mockReturnValue(4);

      expect(fn(1)).toEqual('yay!')
      expect(fn(2)).toEqual('nay!')

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
      • V
        Vikash Sharma

        thank you for taking time and responding, I saw jest-when but didn't really feel to use it. thanks for the recommendation and happy holidays and be safe :)

        👆 you can use Markdown here

        Your comment is awaiting moderation. Thanks!
  • E
    Esai

    how to mock lambda function using jest

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Hi Esai. There is nothing magical about Lambda functions. The trickiest part is to get the right shape of an event as a test value. I like to use sam local generate-event to help me with this one. It is one of the cool features of AWS SAM framework. Other than that, Lambdas are easy to test pure functions.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • P
    Papu Kumar

    How to mock toJSON() function?

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
  • J
    Jacek

    Hi Pawel,

    Thanks for the neat summary!

    I just wanted to relate to this:

    jest.spyOn() allows us to restore the initial implementation using mockRestore() method. This is not something that we can do using jest.fn().

    I have just yesterday had a situation where I needed to mock esModule, which is not possible, as far as I know, with jest.mock(), and then restore the original implementation in one of the next test in the same suite. After some time of research, I found this way to do it:

    // Module that will be mocked and unmocked
    import exampleModule from 'modules/exampleModule';
    
    const ActualExampleModule = jest.requireActual('modules/exampleModule');
    
    describe('Some tests that required mocked module', () => {
      // Tests on mock
    });
    
    describe('Some tests that required original module', () => {
      it('Test with restored module', async () => {
        const restoredModule = await import('modules/exampleModule');
        restoredModule.default = ActualExampleModule .default;
    
        // Now we can assert on original module
      });
    });
    

    Hope that helps someone!

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Thank you for sharing your insights Jacek.

      I am not sure how much Jest supports ES modules at this point, but I would expect some big changes on this space in relatively short future, as ES modules re more and more popular within Node.js ecosystem.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • N
    NewToJest

    Thank you! The jest.spyOn saved me! I couldn't figure out for the life of me on how to mock the implementation of an Angular method.

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
  • S
    Stefany

    Hello, sorry I don´t understand the differences between .mock() and .createMockFromModule(), seems to me that the only difference is that .mock() takes more parameters (jest.mock(moduleName, factory, options)) and you can pass the mocked implementation in the factory parameter but both work exactly the same. Can you please tell me the differences? I'd really appreciated it :)

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Hi. The jest.mock() is explicit and manual way of mock creation, where the createMockFromModue() is a helper that automatically mocks all the members exported from the module for you. They are not one to one.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • A
    Adarsh

    Could you please provide some insight on best way to mock functions in a file that are not exported

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      I am not sure if thats even possible. If you don't have an access to a direct member, I would suggest looking into other patters, like dependency injection that will allow you to entirely swap an implementation of a tested unit.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • r
    rubickecho

    hi, i want to ask a question suppose the demo.js file has the following code:

    export function a() {
      b();
    }
    
    export function b() {}
    

    How have I tested that when a is executed, b is called correctly? The scheme given by ai is like this, but I did not pass it

    import * as demo from "./demo.js";
    
    test("a calls b", () => {
      const mockB = jest.fn();
      demo.b = mockB;
      demo.a();
      expect(mockB).toHaveBeenCalled();
    });
    

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      This is tricky, especially when it comes to testing ESM, which is just experimental on jest at the time of writing this. Look at this comment please.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!

Leave a comment

👆 you can use Markdown here

Your comment is awaiting moderation. Thanks!