How can I mock dependencies for unit testing in RequireJS?

JavascriptUnit TestingMockingRequirejs

Javascript Problem Overview


I have an AMD module I want to test, but I want to mock out its dependencies instead of loading the actual dependencies. I am using requirejs, and the code for my module looks something like this:

define(['hurp', 'durp'], function(Hurp, Durp) {
  return {
    foo: function () {
      console.log(Hurp.beans)
    },
    bar: function () {
      console.log(Durp.beans)
    }
  }
}

How can I mock out hurp and durp so I can effectively unit test?

Javascript Solutions


Solution 1 - Javascript

So after reading this post I came up with a solution that use the requirejs config function to create a new context for your test where you can simply mock your dependencies:

var cnt = 0;
function createContext(stubs) {
  cnt++;
  var map = {};

  var i18n = stubs.i18n;
  stubs.i18n = {
    load: sinon.spy(function(name, req, onLoad) {
      onLoad(i18n);
    })
  };

  _.each(stubs, function(value, key) {
    var stubName = 'stub' + key + cnt;

    map[key] = stubName;

    define(stubName, function() {
      return value;
    });
  });

  return require.config({
    context: "context_" + cnt,
    map: {
      "*": map
    },
    baseUrl: 'js/cfe/app/'
  });
}

So it creates a new context where the definitions for Hurp and Durp will be set by the objects you passed into the function. The Math.random for the name is maybe a bit dirty but it works. Cause if you'll have a bunch of test you need to create new context for every suite to prevent reusing your mocks, or to load mocks when you want the real requirejs module.

In your case it would look like this:

(function () {

  var stubs =  {
    hurp: 'hurp',
    durp: 'durp'
  };
  var context = createContext(stubs);

  context(['yourModuleName'], function (yourModule) {

    //your normal jasmine test starts here

    describe("yourModuleName", function () {
      it('should log', function(){
         spyOn(console, 'log');
         yourModule.foo();

         expect(console.log).toHasBeenCalledWith('hurp');
      })
    });
  });
})();
   

So I'm using this approach in production for a while and its really robust.

Solution 2 - Javascript

you might want to check out the new Squire.js lib

from the docs:

Squire.js is a dependency injector for Require.js users to make mocking dependencies easy!

Solution 3 - Javascript

I have found three different solutions to this problem, none of them pleasant.

Defining Dependencies Inline

define('hurp', [], function () {
  return {
    beans: 'Beans'
  };
});

define('durp', [], function () {
  return {
    beans: 'durp beans'
  };
});

require('hurpdhurp', function () {
  // test hurpdurp in here
});

Fugly. You have to clutter up your tests with lots of AMD boilerplate.

Loading Mock Dependencies From Different Paths

This involves using a separate config.js file to define paths for each of the dependencies that point to mocks instead of the original dependencies. This is also ugly, requiring the creation of tons of test files and configurations files.

Fake It In Node

This is my current solution, but is still a terrible one.

You create your own define function to provide your own mocks to the module and put your tests in the callback. Then you eval the module to run your tests, like so:

var fs = require('fs')
  , hurp = {
      beans: 'BEANS'
    }
  , durp = {
      beans: 'durp beans'
    }
  , hurpDurp = fs.readFileSync('path/to/hurpDurp', 'utf8');
  ;



function define(deps, cb) {
  var TestableHurpDurp = cb(hurp, durp);
  // now run tests below on TestableHurpDurp, which is using your
  // passed-in mocks as dependencies.
}

// evaluate the AMD module, running your mocked define function and your tests.
eval(hurpDurp);

This is my preferred solution. It looks a little magic, but it has a few benefits.

  1. Run your tests in node, so no messing with browser automation.
  2. Less need for messy AMD boilerplate in your tests.
  3. You get to use eval in anger, and imagine Crockford exploding with rage.

It still has some drawbacks, obviously.

  1. Since you are testing in node, you can't do anything with browser events or DOM manipulation. Only good for testing logic.
  2. Still a little clunky to set up. You need to mock out define in every test, since that is where your tests actually run.

I am working on a test runner to give a nicer syntax for this kind of stuff, but I still have no good solution for problem 1.

Conclusion

Mocking deps in requirejs sucks hard. I found a way that sortof works, but am still not very happy with it. Please let me know if you have any better ideas.

Solution 4 - Javascript

There's a config.map option http://requirejs.org/docs/api.html#config-map.

On how-to use it:

  1. Define normal module;

  2. Define stub module;

  3. Configure RequireJS expicitely;

    requirejs.config({ map: { 'source/js': { 'foo': 'normalModule' }, 'source/test': { 'foo': 'stubModule' } } });

In this case for normal and test code you could use the foo module which will be real module reference and stub accordingly.

Solution 5 - Javascript

You can use testr.js to mock dependencies. You can set testr to load the mock dependencies instead of the original ones. Here is an example usage:

var fakeDep = function(){
    this.getText = function(){
        return 'Fake Dependancy';
    };
};

var Module1 = testr('module1', {
    'dependancies/dependancy1':fakeDep
});

Check out this as well: http://cyberasylum.janithw.com/mocking-requirejs-dependencies-for-unit-testing/

Solution 6 - Javascript

This answer is based on Andreas Köberle's answer.
It wasn't that easy for me to implement and understand his solution, so I'll explain it in a bit more detail how it works, and some pitfalls to avoid, hoping that it will help future visitors.

So, first of all the setup:
I'm using Karma as test runner and MochaJs as test framework.

Using something like Squire didn't work for me, for some reason, when I used it, the test framework threw errors:

> TypeError: Cannot read property 'call' of undefined

RequireJs has the possibility to map module ids to other module ids. It also allows to create a require function that uses a different config than the global require.
These features is crucial for this solution to work.

Here is my version of the mock code, including (a lot) comments (I hope its understandable). I wrapped it inside a module, so that the tests can easily require it.

define([], function () {
    var count = 0;
    var requireJsMock= Object.create(null);
    requireJsMock.createMockRequire = function (mocks) {
        //mocks is an object with the module ids/paths as keys, and the module as value
        count++;
        var map = {};

        //register the mocks with unique names, and create a mapping from the mocked module id to the mock module id
        //this will cause RequireJs to load the mock module instead of the real one
        for (property in mocks) {
            if (mocks.hasOwnProperty(property)) {
                var moduleId = property;  //the object property is the module id
                var module = mocks[property];   //the value is the mock
                var stubId = 'stub' + moduleId + count;   //create a unique name to register the module

                map[moduleId] = stubId;   //add to the mapping

                //register the mock with the unique id, so that RequireJs can actually call it
                define(stubId, function () {
                    return module;
                });
            }
        }

        var defaultContext = requirejs.s.contexts._.config;
        var requireMockContext = { baseUrl: defaultContext.baseUrl };   //use the baseUrl of the global RequireJs config, so that it doesn't have to be repeated here
        requireMockContext.context = "context_" + count;    //use a unique context name, so that the configs dont overlap
        //use the mapping for all modules
        requireMockContext.map = {
            "*": map
        };
        return require.config(requireMockContext);  //create a require function that uses the new config
    };

    return requireJsMock;
});

The biggest pitfall I encountered, which literally cost me hours, was creating the RequireJs config. I tried to (deep) copy it, and only override the necessary properties (like context or map). This does not work! Only copy the baseUrl, this works fine.

Usage

To use it, require it in your test, create the mocks, and then pass it to createMockRequire. For example:

var ModuleMock = function () {
	this.method = function () {
		methodCalled += 1;
	};
};
var mocks = {
	"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);

And here an example of a complete test file:

define(["chai", "requireJsMock"], function (chai, requireJsMock) {
    var expect = chai.expect;

    describe("Module", function () {
        describe("Method", function () {
            it("should work", function () {
                return new Promise(function (resolve, reject) {
                    var handler = { handle: function () { } };

                    var called = 0;
                    var moduleBMock = function () {
						this.method = function () {
							methodCalled += 1;
						};
					};
					var mocks = {
						"ModuleBIdOrPath": moduleBMock
					}
					var requireMocks = requireJsMock.createMockRequire(mocks);

                    requireMocks(["js/ModuleA"], function (moduleA) {
                        try {
                            moduleA.method();	//moduleA should call method of moduleBMock
                            expect(called).to.equal(1);
                            resolve();
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            });
        });
    });
});

Solution 7 - Javascript

if you want to make some plain js tests which isolate one unit, then you can simply use this snippet:

function define(args, func){
    if(!args.length){
        throw new Error("please stick to the require.js api which wants a: define(['mydependency'], function(){})");
    }

    var fileName = document.scripts[document.scripts.length-1].src;

    // get rid of the url and path elements
    fileName = fileName.split("/");
    fileName = fileName[fileName.length-1];

    // get rid of the file ending
    fileName = fileName.split(".");
    fileName = fileName[0];

    window[fileName] = func;
    return func;
}
window.define = define;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjergasonView Question on Stackoverflow
Solution 1 - JavascriptAndreas KöberleView Answer on Stackoverflow
Solution 2 - JavascriptbusticatedView Answer on Stackoverflow
Solution 3 - JavascriptjergasonView Answer on Stackoverflow
Solution 4 - JavascriptArtem OboturovView Answer on Stackoverflow
Solution 5 - JavascriptjanithView Answer on Stackoverflow
Solution 6 - JavascriptDomyseeView Answer on Stackoverflow
Solution 7 - Javascriptuser3033599View Answer on Stackoverflow