Hommer Smith
Hommer Smith

Reputation: 27852

How to undo mocked require in jest?

I am mocking a library by doing this:

let helperFn;
let mock;

beforeEach(() => {
  mock = jest.fn();
  require('./helperFn').default = mock;
})

If I do this in a test, does it mean that from now on within the whole test suite that default function of helperFn will be associated with that mock?

In the Jest documentations I see how to reset the mock, but I don't see how to remove the mock from a required function. I am concerned that from that test on, all the calls into helperFn.default will see that mock.

Upvotes: 1

Views: 1681

Answers (1)

Brian Adams
Brian Adams

Reputation: 45780

ES6 modules

Here is an ES6 example:

helperFn.js

export default () => 'original';

code.js

import helperFn from './helperFn';

export const func = () => helperFn();

code.test.js

import * as helperFnModule from './helperFn';

import { func } from './code';

describe('helperFn mocked', () => {
  let mock;

  beforeEach(() => {
    mock = jest.spyOn(helperFnModule, 'default');
    mock.mockReturnValue('mocked');
  });

  afterEach(() => {
    mock.mockRestore();
  });

  test('func', () => {
    expect(func()).toBe('mocked');  // Success!
  });
});

describe('helperFn not mocked', () => {
  test('func', () => {
    expect(func()).toBe('original');  // Success!
  });
});

Details

Since ES6 imports are live views of the module exports, it is easy to mock an export and then restore it afterwards.


Node.js modules

Here is a Node.js example:

helperFn.js

exports.default = () => 'original';

code.js

const helperFn = require('./helperFn').default;

exports.func = () => helperFn();

code.test.js

describe('helperFn mocked', () => {
  beforeEach(() => {
    const helperFnModule = require('./helperFn');
    helperFnModule.default = jest.fn(() => 'mocked');
  });

  afterEach(() => {
    jest.resetModules();
  });

  test('func', () => {
    const { func } = require('./code');
    expect(func()).toBe('mocked');  // Success!
  });
});

describe('helperFn not mocked', () => {
  test('func', () => {
    const { func } = require('./code');
    expect(func()).toBe('original');  // Success!
  });
});

Details

The default export gets remembered by code.js when it runs, so changing the default export of helperFn.js doesn't affect func once code.js is required. Jest also caches modules and returns the same module for multiple require calls unless jest.resetModules is called.

So for Node.js modules it is often easiest to require code within the test itself and use jest.resetModules to reset any mocking.

Upvotes: 3

Related Questions