How to mock dependencies for unit tests with ES6 Modules

28,151

Solution 1

I actually got this to work by dropping Jest and going with Karma + Jasmine + Webpack and using https://github.com/jhnns/rewire to mock dependencies

Solution 2

I've started employing the import * as obj style within my tests, which imports all exports from a module as properties of an object which can then be mocked. I find this to be a lot cleaner than using something like rewire or proxyquire or any similar technique.

I can't speak for traceur which was the framework used in the question, but I've found this to work with my setup of Karma, Jasmine, and Babel, and I'm posting it here as this seems to be the most popular question of this type.

I've used this strategy most often when needing to mock Redux actions. Here's a short example:

import * as exports from 'module-you-want-to-mock';
import SystemUnderTest from 'module-that-uses-above-module';

describe('your module', () => {
  beforeEach(() => {
    spyOn(exports, 'someNamedExport');  // mock a named export
    spyOn(exports, 'default');          // mock the default export
  });
  // ... now the above functions are mocked
});

Solution 3

If you are using Webpack another option that has a little more flexibility than rewire is inject-loader.

For example, in a test that is bundled with Webpack:

describe('when an alert is dismissed', () => {

  // Override Alert as we need to mock dependencies for these tests
  let Alert, mockPubSub

  beforeEach(() => {
    mockPubSub = {}
    Alert =  require('inject!./alert')({
      'pubsub-js': mockPubSub
    }).Alert
  })

  it('should publish \'app.clearalerts\'', () => {
    mockPubSub.publish = jasmine.createSpy()
    [...]
    expect(mockPubSub.publish).toHaveBeenCalled()
  })
})

inject-loader, in a similar manner to proxyquire at least allows one to inject dependencies before importing whereas in rewire you must import first and then rewire which makes mocking some components (e.g. those that have some initialization) impossible.

Solution 4

Hi you could use proxyquire:

import assert from 'assert';
import sinon from 'sinon';
import Proxyquire from 'proxyquire';

let proxyquire = Proxyquire.noCallThru(),
    pathModelLoader = './model_loader';

describe('ModelLoader module.', () => {
    it('Should load all models.', () => {
        let fs, modelLoader, ModelLoader, spy, path;
        fs = {
            readdirSync(path) {
                return ['user.js'];
            }
        };
        path = {
            parse(data) {
                return {name: 'user'};
            }
        };
        ModelLoader = proxyquire(pathModelLoader, {'fs': fs, 'path': path});
        modelLoader = new ModelLoader.default();
        spy = sinon.spy(modelLoader, 'loadModels');
        modelLoader.loadModels();
        assert(spy.called);
    });
});

Solution 5

Proxyquire will help you, but it not gonna to work with modern webpack+ES6 modules, ie "aliases".

import fs from 'fs';
import reducers from 'core/reducers';
...
proxyquire('../module1', {
  'fs': mockFs,  // this gonna work
  'core/reducers': mockReducers // what about this?
});

that will not work. As long you can mock fs - you cannot mock reducers. You have to specify real name of dependency, after any webpack or babel transformation. Normally - name relative to module1 location. May be '../../../shared/core/reducers'. May be not.

There is drop in solutions - https://github.com/theKashey/proxyquire-webpack-alias (stable, based on fork of proxyquire) or https://github.com/theKashey/resolveQuire (less stable, can be run upon original proxyquire)

Both of them works as well, and will mock any ES6 module(they are dam good) in a proxyquire way(it is a good way)

Share:
28,151
Evan Layman
Author by

Evan Layman

Updated on July 08, 2022

Comments

  • Evan Layman
    Evan Layman almost 2 years

    I'm trying to fiddle with Ecmascript 6 modules using webpack + traceur to transpile to ES5 CommonJS, but I'm having trouble successfully unit testing them.

    I tried using Jest + traceur preprocessor, but the automocking and dependency names seem to get screwy, plus I can't seem to get sourceMaps to work with Jest and node-inspector debugging.

    Is there a better framework to unit test ES6 modules?