What is mocking? Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.
Mock the dependencies Unit testing is born to test the behavior of a single component — SUT — without caring of its dependencies. The purpose of mocking the dependencies is to keep the SUT isolated from external objects.
Don't mock all mutable dependenciesYou shouldn't mock all mutable dependencies. To see why, we need to look at two types of communications in a typical application: intra-system and inter-system. Inter-system communications are when your application talks to other applications.
So, what is a unit testing dependency? It's a dependency that you must set up in the test before you can exercise the system under test. Dependencies can be explicit, like in the example above, but they also can be implicit.
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.
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.
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.
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.
eval
in anger, and imagine Crockford exploding with rage.It still has some drawbacks, obviously.
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.
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.
There's a config.map
option http://requirejs.org/docs/api.html#config-map.
On how-to use it:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With