Mocking Dependencies For Unit Testing With require.js
I am trying to get a large Backbone app under test, and have been wrestling with requirejs to figure out how to mock dependencies.
Let’s say 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 should I mock out hurp
and durp
so I can unit test?
The Solution(s)
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 (HurpDurp) {
// test hurpdurp in here
});
This is fugly. You have to clutter up your tests with lots of requirejs boilerplate in addition to just defining your mocks.
Loading Mock Dependencies From Different Paths
This involves using a separate require.config
to define paths for each of the
dependencies that point to mocks instead of the original dependencies. This is
also fugly, requiring the creation of tons of test files and configurations
files.
Fake It In Node
This is my current solution, but is still far from ideal.
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 looks a little funky, but it has a few benefits.
- Run your tests in node, so no messing with browser automation.
- Less need for messy AMD boilerplate in your tests.
- You get to use
eval
in anger, and imagine Crockford’s disapproving glare.
It still has some drawbacks, obviously.
- Since you are testing in node, you can’t do anything with browser events or DOM manipulation. Only good for testing logic.
- 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.