Mocking ES6 module dependencies with import * from
This is part one of “Mocking ES6 module dependencies” series. The other parts are:
2. Mocking ES6 module dependencies with proxyquire
3. Mocking same module ES6 dependencies
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.
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.
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:
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:
Then a unit test for module1
which mocks export1
would look like this:
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.
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'
.
After that, we call myFunc
and verify that it calls our mocked instance of export_func1
rather than real one.
Finally, we reset our mocked function to get it ready for the next test / iteration.
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.
Originally published at ozmoroz.com
Other helpful React tips:
Get a weekly email with 8 links to the best articles related to frontend development at http://frontendweekly.co/