We need unit testing for JavaScript files with mock dependencies without a browser.
A simple phrase with a lot of meaning said to me by the head of R&D department. By that time I already had a reasonably large code base of JavaScript files, interdependent and managed by RequireJS, and a tests folder with specs in Jasmine. But that wasn’t even close to the set goal.
Luckily for us, cases for which solutions do not exist yet are very rare. And that wasn’t one of them. Every component required to tackle the task existed separately - essential ingredients of success are straightforward:
There are modules for node that allow running the require.js library and jasmine, but separately. However, the tricky part is to assemble them together. Two github repositories will help solving that:
- AMD-Testing - A demonstration project of how to test async modules (AMD) both in the browser and in NodeJS.
- Squire - A dependency injector for RequireJS users to make mocking dependencies.
So, the bottom line is to build a testing system to run unit tests based on Jasmine specs, intended for JavaScript source code, using either real or mock dependencies in NodeJS environment.
Project Structure
The whole testing system is located in tests folder sitting inside the project folder, for which the unit tests are written. It has several folders and a bootstrapping file.

JavaScript files under unit test might contain some dependencies that are used for handy development, e.g. custom logging system instead of simple console.log function. Such files should always be substituted with mock dependencies while running a unit test. These plugs are stored in mocks folder.
External libraries like jasmine, reporter, which come from amd-testing library for generating terminal suited report, require.js, and squire are under libs folder.
Specs posses jasmine specification files for unit tests.
Finally, runner.js is a launchable file for NodeJS that specifies the configuration for RequireJS, and loads Jasmine spec suits.
Runner File
Script runner.js is the heart of the testing system. Conceptually, it has 3 parts: RequireJS configuration object, global assignments of Jasmine methods, and lastly, specs launch.
RequireJS configuration
Since the original project is managed with RequireJS, we need to utilize it inside a unit test system as well. So the first thing we should do in runner.js is to specify the configuration for requirejs as well. It almost repeats the config object of the original project with paths to the original scripts with minor changes:
- Original paths should be changed to mock objects that are located in the mock folder
- Add paths to specification folders
requirejs.config({
nodeRequire: require,
baseUrl: 'tests',
shim: {
'underscore': {
exports: '_'
},
'backbone': {
deps: ['underscore', 'dom'],
exports: 'Backbone'
},
'modernizr': {
exports: 'Modernizr'
},
'logger': {
exports: 'log4javascript'
}
},
paths: {
// Mocks definitions
logger: './mocks/logger',
sys: '.././utils/sys',
// Real definitions
core: '.././core',
sandbox: '.././sandbox',
jquery: '.././libs/jquery-1.8.2',
underscore: '.././libs/underscore-1.4.2',
text: '.././libs/text-2.0.3',
backbone: '.././libs/backbone-0.9.2',
modernizr: '.././libs/modernizr-2.6.1',
base: '.././managers/base',
mediator_mngr: '.././managers/mediator',
desktop_mngr: '.././managers/desktop',
auth_mngr: '.././managers/auth',
def_mngr: '.././managers/definition',
// Specification definitions
authMngrSpec: './specs/managers/auth',
mediatorMngrSpec: './specs/managers/mediator'
}
});
Globals
Setting globals methods is required to expose RequireJS and Jasmine throughout the testing system, and specs in particular.
// Make define available globally like it is in the browser
global.define = require('requirejs');
// Make jasmine available globally like it is in the browser
global.describe = require('./libs/jasmine').describe;
global.it = require('./libs/jasmine').it;
global.expect = require('./libs/jasmine').expect;
global.beforeEach = require('./libs/jasmine').beforeEach;
global.jasmine = require('./libs/jasmine').jasmine;
Runs
The last part of runner.js file loads Jasmine specification suits that should run, and the script of the reporter for outputting test results into a console/terminal.
/* Main function which loads and runs specification for unit test */
requirejs(['authMngrSpec', 'mediatorMngrSpec'], function() {
setTimeout(function () {
var Reporter = require('./libs/reporter').ConsoleJasmineReporter;
global.jasmine.getEnv().addReporter(new Reporter());
global.jasmine.getEnv().execute();
}, 50);
});
Unit test
Unit tests can be written in two ways: using original dependencies and using mock dependencies. The former is not unique in any way from writing regular Jasmine spec.
define(['.././managers/mediator'], function (mediator) {
describe('Mediator', function () {
beforeEach(function () {
// before code goes here
});
describe('subscribe method', function () {
describe('verify parameters', function () {
it('should throw exception if all parameters are not specified', function () {
// code
});
});
});
});
return 'Mediator Manager';
});
The define statement loads the script that should be tested. If the script has dependencies, they will be loaded as well either using relative path or paths, which were specified in the configuration object. The define callback function specifies the unit testing code.
Writing unit test with mock dependencies requires little more coding.
Unit Tests With Mocks
Writing unit tests with mock dependencies consist of two obvious stages - creating mock deps and building unit test. The latter is no different from described above, so there is no need to detail it again.
define(['./libs/squire'], function (Squire) {
var injector = new Squire();
injector.mock('base', {
data: {
ajax: function (params) {
console.log('I did ajax request to ' + params.url);
}
}
});
injector.mock('mediator_mngr', {});
injector.require(['.././managers/auth'], function (auth) {
describe('Authorization manager', function () {
beforeEach(function () {
expect(auth).toBeDefined();
});
describe('login method', function () {
it('should succeed', function () {
auth.login(TEST_SUCCESS_CREDENTIALS, function (res) {
expect(res.status).toBe('200');
}, {});
});
});
describe('logout method', function () {});
});
});
return 'Authorization Manager';
});
Firstly, Squire lib should be loaded and instantiated. Its documentation page provides a good explanation of how to create mocks. What we need is squire.mock method(Object name, Mock object). After creation, the module in test will use mocks, which are identified by mock names, instead of real dependencies. Injector.require method loads the script which should be tested and injects our mocks. The regular unit test code goes further.
Conclusion

Now it’s all done. The only thing remaining is to launch the test. Open a terminal, navigate to the test folder, type `node runner.js`, and enjoy the results.