How can I mock an ES6 module import using Jest?

324,814

Solution 1

Fast forwarding to 2020, I found this blog post to be the solution: Jest mock default and named export

Using only ES6 module syntax:

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};

// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

Also one thing you need to know (which took me a while to figure out) is that you can't call jest.mock() inside the test; you must call it at the top level of the module. However, you can call mockImplementation() inside individual tests if you want to set up different mocks for different tests.

Solution 2

Edit: Several years have passed and this isn't really the right way to do this any more (and probably never was, my bad).

Mutating an imported module is nasty and can lead to side effects like tests that pass or fail depending on execution order.

I'm leaving this answer in its original form for historical purposes, but you should really use jest.spyOn or jest.mock. Refer to the jest docs or the other answers on this page for details.

Original answer follows:


I've been able to solve this by using a hack involving import *. It even works for both named and default exports!

For a named export:

// dependency.js
export const doSomething = (y) => console.log(y)
// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Or for a default export:

// dependency.js
export default (y) => console.log(y)
// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

Solution 3

You have to mock the module and set the spy by yourself:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Solution 4

To mock an ES6 dependency module default export using Jest:

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

The other options didn't work for my case.

Solution 5

Adding more to Andreas' answer. I had the same problem with ES6 code, but I did not want to mutate the imports. That looked hacky. So I did this:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

And added file dependency.js in the " __ mocks __" folder parallel to file dependency.js. This worked for me. Also, this gave me the option to return suitable data from the mock implementation. Make sure you give the correct path to the module you want to mock.

Share:
324,814
Cam Jackson
Author by

Cam Jackson

https://camjackson.net

Updated on July 26, 2022

Comments

  • Cam Jackson
    Cam Jackson almost 2 years

    I want to test that one of my ES6 modules calls another ES6 module in a particular way. With Jasmine this is super easy --

    The application code:

    // myModule.js
    import dependency from './dependency';
    
    export default (x) => {
      dependency.doSomething(x * 2);
    }
    

    And the test code:

    //myModule-test.js
    import myModule from '../myModule';
    import dependency from '../dependency';
    
    describe('myModule', () => {
      it('calls the dependency with double the input', () => {
        spyOn(dependency, 'doSomething');
    
        myModule(2);
    
        expect(dependency.doSomething).toHaveBeenCalledWith(4);
      });
    });
    

    What's the equivalent with Jest? I feel like this is such a simple thing to want to do, but I've been tearing my hair out trying to figure it out.

    The closest I've come is by replacing the imports with requires, and moving them inside the tests/functions. Neither of which are things I want to do.

    // myModule.js
    export default (x) => {
      const dependency = require('./dependency'); // Yuck
      dependency.doSomething(x * 2);
    }
    
    //myModule-test.js
    describe('myModule', () => {
      it('calls the dependency with double the input', () => {
        jest.mock('../dependency');
    
        myModule(2);
    
        const dependency = require('../dependency'); // Also yuck
        expect(dependency.doSomething).toBeCalledWith(4);
      });
    });
    

    For bonus points, I'd love to make the whole thing work when the function inside dependency.js is a default export. However, I know that spying on default exports doesn't work in Jasmine (or at least I could never get it to work), so I'm not holding out hope that it's possible in Jest either.