When using jest with ES6 modules and babel-jest, all the jest.mock calls are hoisted.
Let's say I'd like to mock fs module for the tested class, but preserve the original implementation for the rest of the modules (for example some utils that I use during the test).
Consider the following example:
class UnderTest {
someFunction(){
fs.existsSync('blah');
}
}
class TestUtility {
someOtherFunction(){
fs.existsSync('blahblah');
}
}
the test:
it('Should test someFunction with mocked fs while using TestUtility'', () => {
testUtility.someOtherFunction(); // Should work as expected
underTest.someFunction(); // Should work with mock implementation of 'fs'
})
Now, one would expect that with the following approach the fs module will be mocked for UnderTest but not for TestUtility.
import {TestUtility} from './test-utility';
jest.mock('fs');
import {UnderTest } from './under-test';
However, due to the hoisting, fs module will be mocked for all the modules (which is undesirable).
Is there any way to achieve the described behavior?
To opt out from mocking a module in a test jest.doMock(moduleName, factory, options) and jest.dontMock(moduleName) should be used.
jest.doMock(moduleName, factory, options)
When using babel-jest, calls to mock will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior.
jest.dontMock(moduleName)
When using babel-jest, calls to unmock will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior.
So in your case I would try something like
beforeEach(() => {
jest.resetModules();
});
it('Should test someFunction with mocked fs while using TestUtility'', () => {
jest.dontMock('fs');
testUtility.someOtherFunction(); // Should work as expected
jest.doMock('fs', () => {
return ... // return your fs mock implementation;
});
underTest.someFunction(); // Should work with mock implementation of 'fs'
})
You should probably use jest's requireActual:
const fs = jest.requireActual('fs'); // Unmockable version of jest
class TestUtility {
someOtherFunction(){
fs.existsSync('blahblah');
}
}
From the documentation:
Jest allows you to mock out whole modules in your tests, which can be useful for testing your code is calling functions from that module correctly. However, sometimes you may want to use parts of a mocked module in your test file, in which case you want to access the original implementation, rather than a mocked version.
Related
I am mocking a module using Jest because it contains code that shouldn't run in the test. However I can see from the output that code in the module is being run.
// foo.js
console.log('Hello')
// test.js
jest.mock('./foo')
const foo = require('./foo')
test.todo('write some tests')
Console output
PASS test.js
✎ todo 1 test
console.log foo.js:1
Hello
What's up with that?
This has tripped me up a couple of times.
If you don't provide a mock implementation to jest.mock it will return an object which mirrors the exports of the mocked module but with every function replaced with a mock jest.fn(). This is pretty neat as it is often what you want. But in order to determine the exports of the module, it must first require it. This is what is causing the console.log to be run.
Two possible solutions:
Don't run code in the top level of the module: instead export a function which runs the code.
Provide your own mock implementation so it doesn't need to introspect the module jest.mock('./foo', () => {})
How can I mock a function that is being imported inside the file that contains the function I am testing?
without putting it in the mocks folder.
// FileIWantToTest.js
import { externalFunction } from '../../differentFolder';
export const methodIwantToTest = (x) => { externalFunction(x + 1) }
I need to make sure that externalFunction is called and its called with the correct arguments.
Seems super simple but the documentation doesn't cover how to do this without mocking the module for all the files in the test by putting the mocks in the mocks folder.
The solution: I need to take my jest.mock call outside of any test or any other function because jest needs to hoist it. Also for named exports like in my case I also have to use the following syntax:
jest.mock('../../differentFolder', () => ({
__esModule: true,
externalFunction: jest.fn(),
}));
One of the easiest approaches is to import the library and use jest.spyOn to spy on the method:
import { methodIwantToTest } from './FileIWantToTest';
import * as lib from '../../differentFolder'; // import the library containing externalFunction
test('methodIwantToTest', () => {
const spy = jest.spyOn(lib, 'externalFunction'); // spy on externalFunction
methodIwantToTest(1);
expect(spy).toHaveBeenCalledWith(2); // SUCCESS
});
I have a Jest test file like the following:
// utils.test.js
let utils = require('./utils')
jest.mock('./utils')
test('print items', () => {
utils.printItems(['a'])
expect(utils.getImage).toHaveBeenLastCalledWith('a.png')
})
test('get image', () => {
utils = require.requireActual('./utils')
// `utils` is still mocked here for some reason.
expect(utils.getImage('note.png')).toBe('note')
})
And a mock like this:
// __mocks__/utils.js
const utils = require.requireActual('../utils');
utils.getImage = jest.fn(() => 'abc');
module.exports = utils;
Yet as you can see in my comment in the second test, utils is still the mocked version rather than the actual version of the module. Why is that? How can I get it to be the actual version, rather than the mocked version?
You still get the mocked utils module in your second test because you have actually required it inside the manual mock (__mocks__/utils.js), which in Jest's cache is still referenced as a mock that should be returned due to jest.mock() being at the top most scope.
A way to fix it is either to not use the module in a manual mock, or update your second test to unmock and require a new version of it. For example:
test('get image', () => {
jest.unmock('./utils')
const utils = require.requireActual('./utils')
// `utils` is now the original version of that module
expect(utils.getImage('note.png')).toBe('note')
})
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?
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
});
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.
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);
});
});
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
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)
This is a trivial example that illustrates the crux of my problem:
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.doComplexStuff();
}
module.exports = underTest;
I am trying to write a unit test for this code. How can I mock out the requirement for the innerLib without mocking out the require function entirely?
So this is me trying to mock out the global require and finding out that it won’t work even to do that:
var path = require('path'),
vm = require('vm'),
fs = require('fs'),
indexPath = path.join(__dirname, './underTest');
var globalRequire = require;
require = function(name) {
console.log('require: ' + name);
switch(name) {
case 'connect':
case indexPath:
return globalRequire(name);
break;
}
};
The problem is that the require function inside the underTest.js file has actually not been mocked out. It still points to the global require function. So it seems that I can only mock out the require function within the same file I’m doing the mocking in. If I use the global require to include anything, even after I’ve overridden the local copy, the files being required will still have the global require reference.
You can now!
I published proxyquire which will take care of overriding the global require inside your module while you are testing it.
This means you need no changes to your code in order to inject mocks for required modules.
Proxyquire has a very simple api which allows resolving the module you are trying to test and pass along mocks/stubs for its required modules in one simple step.
#Raynos is right that traditionally you had to resort to not very ideal solutions in order to achieve that or do bottom-up development instead
Which is the main reason why I created proxyquire - to allow top-down test driven development without any hassle.
Have a look at the documentation and the examples in order to gauge if it will fit your needs.
A better option in this case is to mock methods of the module that gets returned.
For better or worse, most node.js modules are singletons; two pieces of code that require() the same module get the same reference to that module.
You can leverage this and use something like sinon to mock out items that are required. mocha test follows:
// in your testfile
var innerLib = require('./path/to/innerLib');
var underTest = require('./path/to/underTest');
var sinon = require('sinon');
describe("underTest", function() {
it("does something", function() {
sinon.stub(innerLib, 'toCrazyCrap').callsFake(function() {
// whatever you would like innerLib.toCrazyCrap to do under test
});
underTest();
sinon.assert.calledOnce(innerLib.toCrazyCrap); // sinon assertion
innerLib.toCrazyCrap.restore(); // restore original functionality
});
});
Sinon has good integration with chai for making assertions, and I wrote a module to integrate sinon with mocha to allow for easier spy/stub cleanup (to avoid test pollution.)
Note that underTest cannot be mocked in the same way, as underTest returns only a function.
Another option is to use Jest mocks. Follow up on their page
I use mock-require. Make sure you define your mocks before you require the module to be tested.
Simple code to mock modules for the curious
Notice the parts where you manipulate the require.cache and note require.resolve method as this is the secret sauce.
class MockModules {
constructor() {
this._resolvedPaths = {}
}
add({ path, mock }) {
const resolvedPath = require.resolve(path)
this._resolvedPaths[resolvedPath] = true
require.cache[resolvedPath] = {
id: resolvedPath,
file: resolvedPath,
loaded: true,
exports: mock
}
}
clear(path) {
const resolvedPath = require.resolve(path)
delete this._resolvedPaths[resolvedPath]
delete require.cache[resolvedPath]
}
clearAll() {
Object.keys(this._resolvedPaths).forEach(resolvedPath =>
delete require.cache[resolvedPath]
)
this._resolvedPaths = {}
}
}
Use like:
describe('#someModuleUsingTheThing', () => {
const mockModules = new MockModules()
beforeAll(() => {
mockModules.add({
// use the same require path as you normally would
path: '../theThing',
// mock return an object with "theThingMethod"
mock: {
theThingMethod: () => true
}
})
})
afterAll(() => {
mockModules.clearAll()
})
it('should do the thing', async () => {
const someModuleUsingTheThing = require('./someModuleUsingTheThing')
expect(someModuleUsingTheThing.theThingMethod()).to.equal(true)
})
})
BUT... jest has this functionality built in and I recommend that testing framework over rolling your own for testing purposes.
Mocking require feels like a nasty hack to me. I would personally try to avoid it and refactor the code to make it more testable.
There are various approaches to handle dependencies.
1) pass dependencies as arguments
function underTest(innerLib) {
return innerLib.doComplexStuff();
}
This will make the code universally testable. The downside is that you need to pass dependencies around, which can make the code look more complicated.
2) implement the module as a class, then use class methods/ properties to obtain dependencies
(This is a contrived example, where class usage is not reasonable, but it conveys the idea)
(ES6 example)
const innerLib = require('./path/to/innerLib')
class underTestClass {
getInnerLib () {
return innerLib
}
underTestMethod () {
return this.getInnerLib().doComplexStuff()
}
}
Now you can easily stub getInnerLib method to test your code.
The code becomes more verbose, but also easier to test.
If you've ever used jest, then you're probably familiar with jest's mock feature.
Using "jest.mock(...)" you can simply specify the string that would occur in a require-statement in your code somewhere and whenever a module is required using that string a mock-object would be returned instead.
For example
jest.mock("firebase-admin", () => {
const a = require("mocked-version-of-firebase-admin");
a.someAdditionalMockedMethod = () => {}
return a;
})
would completely replace all imports/requires of "firebase-admin" with the object you returned from that "factory"-function.
Well, you can do that when using jest because jest creates a runtime around every module it runs and injects a "hooked" version of require into the module, but you wouldn't be able to do this without jest.
I have tried to achieve this with mock-require but for me it didn't work for nested levels in my source. Have a look at the following issue on github: mock-require not always called with Mocha.
To address this I have created two npm-modules you can use to achieve what you want.
You need one babel-plugin and a module mocker.
babel-plugin-mock-require
jestlike-mock
In your .babelrc use the babel-plugin-mock-require plugin with following options:
...
"plugins": [
["babel-plugin-mock-require", { "moduleMocker": "jestlike-mock" }],
...
]
...
and in your test file use the jestlike-mock module like so:
import {jestMocker} from "jestlike-mock";
...
jestMocker.mock("firebase-admin", () => {
const firebase = new (require("firebase-mock").MockFirebaseSdk)();
...
return firebase;
});
...
The jestlike-mock module is still very rudimental and does not have a lot of documentation but there's not much code either. I appreciate any PRs for a more complete feature set. The goal would be to recreate the whole "jest.mock" feature.
In order to see how jest implements that one can look up the code in the "jest-runtime" package. See https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/index.js#L734 for example, here they generate an "automock" of a module.
Hope that helps ;)
You can't. You have to build up your unit test suite so that the lowest modules are tested first and that the higher level modules that require modules are tested afterwards.
You also have to assume that any 3rd party code and node.js itself is well tested.
I presume you'll see mocking frameworks arrive in the near future that overwrite global.require
If you really must inject a mock you can change your code to expose modular scope.
// underTest.js
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.toCrazyCrap();
}
module.exports = underTest;
module.exports.__module = module;
// test.js
function test() {
var underTest = require("underTest");
underTest.__module.innerLib = {
toCrazyCrap: function() { return true; }
};
assert.ok(underTest());
}
Be warned this exposes .__module into your API and any code can access modular scope at their own danger.
You can use mockery library:
describe 'UnderTest', ->
before ->
mockery.enable( warnOnUnregistered: false )
mockery.registerMock('./path/to/innerLib', { doComplexStuff: -> 'Complex result' })
#underTest = require('./path/to/underTest')
it 'should compute complex value', ->
expect(#underTest()).to.eq 'Complex result'
I use a simple factory the returns a function that calls a function with all of its dependencies:
/**
* fnFactory
* Returns a function that calls a function with all of its dependencies.
*/
"use strict";
const fnFactory = ({ target, dependencies }) => () => target(...dependencies);
module.exports = fnFactory;
Wanting to test the following function:
/*
* underTest
*/
"use strict";
const underTest = ( innerLib, millions ) => innerLib.doComplexStuff(millions);
module.exports = underTest;
I would setup my test (I use Jest) as follows:
"use strict";
const fnFactory = require("./fnFactory");
const _underTest = require("./underTest");
test("fnFactory can mock a function by returng a function that calls a function with all its dependencies", () => {
const fake = millions => `Didn't do anything with ${millions} million dollars!`;
const underTest = fnFactory({ target: _underTest, dependencies: [{ doComplexStuff: fake }, 10] });
expect(underTest()).toBe("Didn't do anything with 10 million dollars!");
});
See results of test
In production code I would manually inject the callee's dependencies as below:
/**
* main
* Entry point for the real application.
*/
"use strict";
const underTest = require("./underTest");
const innerLib = require("./innerLib");
underTest(innerLib, 10);
I tend to limit the scope of most of the modules that I write to one thing, which reduces the number of dependencies that have to be accounted for when testing and integrating them into the project.
And that's my approach to dealing with dependencies.