Mocking ES6 module dependencies with import * from

Not so long ago I faced a problem: I needed to mock ES6 module’s dependencies for unit testing. The reason for mocking dependencies in unit tests is the following: when I write a unit test, I want to test the functionality of a single unit of code, hence a unit test. However, if a module has any dependencies, those dependencies need be satisfied. That may mean importing and executing code in other modules. As a result, the unit test loses its purity – the test results will depend not only on the module I’m focusing on but also on the other code my module depends on.

Summary:

  • ES6’s import * from statement provides a convenient way to mock the dependencies of ES6 modules without any 3rd party libraries.
  • However, the modules which are being mocked are always evaluated, which sometimes may be unacceptable.

To mitigate that issue we may want to mock the dependencies of the module we’re testing, i.e. to substitute them with another code with limited functionality. That substitution code may either do nothing at all or verify that certain functions are called in the right way. Some frameworks, like AngularJS, enforce dependency injection design pattern and provide standard ways for mocking dependencies. However, if you are using a framework like React.js, you are out of luck. Dependency injection is not enforced in React, and usually not used. Dependencies are generally imported using Javascript’s require statement or import if you write in ES6. Furthermore, there is no standard way to mock dependencies in React (or Javascript / ES6).

Because there is no standard way, there are, in a typical Javascript manner, quite a few non-standard ones. There are mockery, mock-require, proxyquire , and babel-plugin-rewire to name just a few.  Besides, there are ways to do dependency injection in React. There are eloquent arguments for using dependency injections in React, and equally persuasive counter-arguments. There are lengthy discussions on stackoverflow.com about Javascript and ES6 import mocking.

All the discussions apart, I had a real problem. I write in ES6 with React and JSX and then transpile it into ES5 with Babel. My unit tests are written in ES6, and I use mocha, sinon and Enzyme’s shallow rendering for testing. I don’t use dependency injection. My module dependencies are imported using ES6 import statements. I wanted to be able to mock those dependencies. I started trying various methods and found that most of them didn’t work for me. Some of them didn’t work at all; the others didn’t quite do what I needed. Finally, I found two solutions which got the job done, albeit with some caveats: a standard (kind of) ES6 way which requires no 3rd party libraries, and proxyquire. I will talk about the “standard” way in this article, and about proxyquire in the next one.

import * as …

As documented in this stackoverflow.com answer, ES6 provides constructionimport * as to import all exported items from a module. Once the module is imported, its individual exports can be overridden with mock-ups.

Let’s say we have export1.js module which exports a single function:

console.log('If you can see that, export1.js is evaluated');

export const exportFunc = () => {
  return 'This is real exportFunc from export1';
};

Pay attention to the console.log here. That is to check whether this module is evaluated during the unit test’s execution.

Then there’s another module module1 which imports and calls it:

import { exportFunc } from './export1';

export const myFunc = () => {
  return exportFunc();
};

Then a unit test for module1 which mocks export1 would look like this:

'use strict';
import { myFunc } from './module1';
import * as export1 from './export1';

/* global beforeEach, describe, sinon */

describe.only('import * from - a dependency in another module',
() => {
  let export1Mock;

  beforeEach(() => {
    export1Mock = sinon.mock(export1);
    export1Mock
      .expects('exportFunc')
      .once()
      .returns('This is mocked exportFunc');
  });

  it('exportFunc function should be called', () => {
    myFunc();
    export1Mock.verify();
  });

  afterEach(() => {
    export1Mock.restore();
  });
});

Test completed successfully

That is not very straightforward, but not too bad either. Pretty much like Angular’s $provide syntax.

Let’s unpack what’s going on here.

import { myFunc } from './module1';
import * as export1 from './export1';

Here we import function from module1 we are tesing as usual. However, we use import * as technique to import export1 module containing the dependencies we want to mock. The entire export1 module is imported as export1Mock object.

Then we replace export_func1 of export1Module with a sinon mock. That mock expectes export_func1 to be called once and returns string ‘This is mocked export_func1’.

beforeEach(() => {
  export1Mock = sinon.mock(export1);
  export1Mock
    .expects('exportFunc')
    .once()
    .returns('This is mocked exportFunc');
});

After that, we call myFunc and verify that it calls our mocked instance of export_func1 rather than real one.

it('export_func1 function should be called', () => {
  myFunc();
  export1Mock.verify();
});

Finally, we reset our mocked function to get it ready for the next test / iteration.

afterEach(() => {
  export1Mock.restore();
});

However, there is an issues with this approach. As you may have noticed, we can see the output of the console.log we’ve put into export1.js, that means that the module was evaluated despite the fact we mocked it.

Turns out, the content of modules imported via import * as always gets evaluated included their import statements. That breaks the unit test purity principle: your test will not only depend on the code you’re testing, but on the modules it imports, and the modules those modules import, and so on. In practice that is usually fine if your modules only contain functions. But if any of the modules in your import chain contains any global code, it will get executed, and you may get an error.

In the next part of the series, I will explore an alternative technique which can alleviate the above issue - mocking up Javascript module dependencies with proxyquire.

The source code of the examples for this article can be downloaded from my Github repository.

Other helpful React tips: