How can I mock dependencies for unit testing in RequireJS? How can I mock dependencies for unit testing in RequireJS? javascript javascript

How can I mock dependencies for unit testing in RequireJS?


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.


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!


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.