Mocking in Jest

I used to do my JavaScript testing using a combination of mocha, expect and sinon, but Jest packages all those features into one cohesive package.

Transitioning to Jest has been smooth. The part that took me a while to figure out is how to properly mock methods, and this is what I'm going to develop here.

Mocking direct methods

Imagine a dummy component with two methods, foo and bar, with the following implementation:

const component = {
  foo(input) {
    return {
      id: input,
      name: component.bar()
    }
  },
  bar() {
    const alpha = 'bar';
    const beta = 'baz';
    return `${alpha}-${beta}`;
  }
}
export default component;

Calling foo(42) will return { id: 42, name: 'bar-baz' }.

The bar method is straightforward to test as it does not have any dependencies. All we have to test is that the output is the one we are expecting. The code can be simplified but I'm making it overly complicated here on purpose.

The point is that when we test foo, we don't want to deal with the internals of bar. We should be able to change the internals of bar and have our foo tests still work. Actually, we could even completly change the return value of bar and still have our foo tests pass.

To achieve that decoupling, the trick is to mock the bar method to control its behavior during our test.

import component from './component.js';

describe('component', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should have the name set to the value of bar()', () => {
    // Given
    const input = 42;
    const expected = {
      id: input,
      name: 'my-mock-name'
    };

    // When
    jest
      .spyOn(component, 'bar')
      .mockReturnValue('my-mock-name');
    const actual = component.foo(input);

    // Then
    expect(actual).toEqual(expected);
  });
});

The first step is to call jest.spyOn(component, 'bar'). This will replace the component.bar method with a mock version. By default the mock version will behave like the original method. Spying on a method has other benefits (like being able to assert the number of times a method is called), but those won't be discussed in this article.

Once we've replaced the original method with our spy, we can now call .mockReturnValue('my-mock-name') on it that will change the original method so it now always return my-mock-name when called.

The last step is to call jest.restoreAllMocks() in the afterEach hook. afterEach is called after each test, and restoreAllMocks will restore all our spies to their original methods. If we don't do that, all our component.bar calls in all our tests will always return my-mock-name.

Mocking dependency methods

This first part was about mocking methods of one of our dependencies. But how do you mock sub-dependencies? Let's update our component so it now uses one of its own dependencies:

import dependency from 'dependency';
const component = {
  foo(input) {
    return {
      id: input,
      name: dependency.bar()
    }
  }
}
export default component;

There is no component.bar method anymore here as component is directly calling its dependency .bar method. To mock dependencies, we need a bit more plumbing.

import component from './component.js';
jest.mock('dependency'); // <-- Here

describe('component', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should have the name set to the value of bar()', () => {
    // Given
    const input = 42;
    const expected = {
      id: 42,
      name: 'my-mock-name'
    };

    // When
    const dependency = require('dependency'); // <-- Here
    jest
      .spyOn(dependency, 'bar') // <-- Here
      .mockReturnValue('my-mock-name');
    const actual = component.foo(input);

    // Then
    expect(actual).toEqual(expected);
  });
});

We've added jest.mock('dependency') to our test file. It will tell Jest to replace all require and import of dependency with a mock object. This means that whenever we will import dependency (either in component.js or in our tests), it will be replaced with a mock version.

Like we did in the previous example, we will hardcode the return value of the bar method in our test. This time, we first need to import it (const dependency = require('dependency'), so we can spy on it and mock its return value.

Conclusion

Hope that overview can help others. It took me some time to understand how mocking was working in Jest and I hope this will help other figure out all the pieces.

Tested with Jest v21.1.0


Tags : #jest

Want to add something ? Feel free to get in touch on Twitter : @pixelastic