Skip to content

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!

What'ya think?

👆 you can use Markdown here

Your comment is awaiting moderation. Thanks!