171

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.

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Matthew Taylor
  • 3,911
  • 4
  • 29
  • 33
  • you have to overwrite `global.require`. Variables write to `module` by default as modules are module scoped. – Raynos Apr 21 '11 at 18:27
  • @Raynos How would I do that? global.require is undefined? Even if I replace it with my own function other functions would never use that would they? – HMR Dec 02 '14 at 06:59

9 Answers9

189

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.

Thorsten Lorenz
  • 11,781
  • 8
  • 52
  • 62
  • 6
    I use proxyquire and I can not say enough good things. It saved me! I was tasked to write jasmine-node tests for an app developed in appcelerator Titanium which forces some modules to be absolute paths and many circular dependancies. proxyquire let me stop gap those and mock out the cruft I didn't need for each test. (Explained [here](http://stackoverflow.com/questions/14669390/how-do-you-force-the-absolute-path-for-node-modules/14705870#14705870)). Thank you sooo much! – Sukima Feb 05 '13 at 11:43
  • 1
    very nice @ThorstenLorenz, I'll def. be using `proxyquire`! – bevacqua Mar 20 '13 at 20:01
  • This is just an amazing module! Reminds me Moq library of .NET. – Teoman shipahi Feb 05 '16 at 19:10
  • 3
    For those of you using Webpack, don't spend time researching proxyquire. It doesn't support Webpack. I'm looking into inject-loader instead (https://github.com/plasticine/inject-loader). – Artif3x Jun 16 '17 at 20:23
121

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

Elliot Foster
  • 1,664
  • 1
  • 12
  • 11
  • Isnt't this the most accurate way of doing it? – thertweck Aug 13 '13 at 12:20
  • I'm not sure what you mean, but the most accurate way of doing it would be to not mock anything, but instead allow dependencies to be injected. This is probably easier, though. Please let me know if I misunderstood your question. – Elliot Foster Nov 10 '13 at 19:50
  • 3
    Unfortunately, node.js modules are NOT guaranteed to be singletons, as explained here: http://justjs.com/posts/singletons-in-node-js-modules-cannot-be-trusted-or-why-you-can-t-just-do-var-foo-require-baz-init – FrontierPsycho Oct 27 '14 at 09:37
  • 5
    @FrontierPsycho a few things: First, as far as testing is concerned, the article is irrelevant. As long as you're testing your dependencies (and not dependencies of dependencies) all of your code is going to get the same object back when you `require('some_module')`, because all of your code shares the same node_modules dir. Second, the article is conflating namespace with singletons, which is sort of orthogonal. Third, that article is pretty darn old (as far as node.js is concerned) so what might have been valid back in the day is possibly not valid now. – Elliot Foster Oct 27 '14 at 21:13
  • 2
    Hm. Unless one of us actually digs up code that proves one point or the other, I'd go with your solution of dependency injection, or just simply passing objects around, it's safer and more future proof. – FrontierPsycho Oct 29 '14 at 08:51
  • 1
    I'm not sure what you're asking to be proven. The singleton (cached) nature of node modules is commonly understood. Dependency injection, while a good route, can be a fair amount more boiler plate and more code. DI is more common in statically typed languages, where it's harder to duck-punch spies/stubs/mocks into your code dynamically. Multiple projects that I've done over the last three years use the method described in my answer above. It's the easiest of all methods, though I use it sparingly. – Elliot Foster Oct 29 '14 at 15:13
  • Does this work with ES6 imports also? `import * as innerLib from './path/to/innerLib'` – mucaho Dec 09 '15 at 13:01
  • I'm not sure, @mucaho. I'm not yet using ES6/babel/etc yet. I'd be interested in hearing the answer, though. It'd also be interesting to see if babel and native ES6 behave the same way. – Elliot Foster Feb 18 '16 at 02:37
  • This is not the best approach because using it you can stub the module only once. Once the module is instancianed using `require` you cannot apply stubs to it anymore. Better use rewire (see https://github.com/jhnns/rewire). Using it you can stub the module local variables at any time. – Alexander Elgin Jun 28 '16 at 10:05
  • 1
    I suggest you read up on sinon.js. If you're using sinon (as in the example above) you would either `innerLib.toCrazyCrap.restore()` and restub, or call sinon via `sinon.stub(innerLib, 'toCrazyCrap')` which allows you to change how the stub behaves: `innerLib.toCrazyCrap.returns(false)`. Also, rewire seems to be very much the same as the `proxyquire` extension above. – Elliot Foster Jun 28 '16 at 21:49
  • This doesn't work when you have `innerLib()` in code instead. So without a method call i.e. `.toCrazyCrap`. – mjakic Aug 21 '19 at 07:19
  • very true, @mjakic, because sinon requires an object and an attribute name to stub. You also can't stub out constructors. The workarounds to both are to use static/class methods (including factory methods to wrap constructors) or to use something like `rewire` or `proxyquire` but I've found the latter modules tedious and dangerously fragile. – Elliot Foster Aug 22 '19 at 14:56
12

I use mock-require. Make sure you define your mocks before you require the module to be tested.

Kunal
  • 490
  • 2
  • 9
  • 18
  • Also good to do stop() or stopAll() right away so you don't get a cached file in a test where you don't want the mock. – Justin Kruse Feb 28 '18 at 16:49
4

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.

King Friday
  • 25,132
  • 12
  • 90
  • 84
3

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.

pumbo
  • 3,646
  • 2
  • 25
  • 27
  • 2
    I don't think it's hacky as you presume... this is the very essence of mocking. Mocking required dependencies makes things so simple that it gives control to developer without changing code structure. Your methods are too verbose and therefore hard to reason about. I choose proxyrequire or mock-require over this; i don't see any problem here. Code is clean and easy to reason about and remember most people who read this already have written code that you want them to complicate. If these libs are hackish, then mocking and stubbing is also hackish by your definition & should be stopped. – Emmanuel Mahuni Jan 16 '18 at 07:52
  • 2
    The problem with approach no.1 is that you are passing internal implementation detail up the stack. With multiple layers it then becomes much more complicated to be a consumer of your module. It can work with an IOC container like approach though so that dependencies are automatically injected for you, however it feels like since we already have dependencies injected in node modules via the imports statement, then it makes sense to be able to mock them at that level. – magritte Jan 26 '18 at 15:48
  • 1
    1) This simply moves the problem to another file 2) still loads the other module and thus imposes performance overhead, and possibly causes side effects (like the popular `colors` module which messes with `String.prototype`) – ThomasR Jan 03 '19 at 14:22
3

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.

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 ;)

allesklarbeidir
  • 312
  • 3
  • 10
1

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'
Hirurg103
  • 4,783
  • 2
  • 34
  • 50
1

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.

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • 2
    Assuming that third party code is well tested isn't a great way to work IMO. – henry.oswald Jul 28 '12 at 20:41
  • 5
    @beck it's a great way to work. It forces you to only work with high quality third party code or write all the pieces of your code so that every dependency is well tested – Raynos Jul 29 '12 at 05:11
  • Ok I thought you were referring to not doing integration tests between your code and the third party code. Agreed. – henry.oswald Jul 29 '12 at 09:38
  • 1
    A "unit test suite" is just a collection of unit tests, but the unit tests should be independent from each other, hence the unit in unit test. To be usable, unit tests shall be fast and independent, so that you can clearly see where the code is broken when a unit test fails. – Andreas Berheim Brudin Apr 30 '15 at 13:14
  • This didn't work for me. The module object doesn't expose the "var innerLib..." etc. – AnitKryst Sep 18 '15 at 17:44
0

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.

4awpawz
  • 11
  • 4