I have a JavaScript project which I want to observe the TDD methodology. I chose the karma framework and requirejs library for this and followed an example demonstrated in the karma docs here.
There is an example of one unit-test file, which is:
define(['app', 'jquery', 'underscore'], function(App, $, _) {
describe('just checking', function() {
it('works for app', function() {
var el = $('<div></div>');
var app = new App(el);
app.render();
expect(el.text()).toEqual('require.js up and running');
});
it('works for underscore', function() {
// just checking that _ works
expect(_.size([1,2,3])).toEqual(3);
});
});
});
The main problem with this approach is that there is no way to clear the App module for each test. So if there are some changes in the App (like closure variables changes etc.) in one it
call, then they can affect other it
call.
Could anyone suggest something on this? These are such popular tools, I can't believe no one ever ran into such a situation.
So, to recapitulate the question, I would like to ask for an answer for any of these more specific ones:
it
function calls) on them ?With some help from Google I've found a solution to this. I cannot say that it is ideal, but it works at least and in each test a required module is in it's pristine state.
First, one need to have his main.js
test file like this, it is almost the same as in the docs, except tests are not defined as modules and are not included as dependencies:
require.config({file
baseUrl: '/base',
paths: {},
shim: {},
deps: [],
callback: window.__karma__.start
});
In the main karma config must be present this (one need to adapt his own paths)
files: [
{pattern: 'src/**/*.js', included: false},
'tests/unit/**/*.js'
],
And in the tests themselves we leverage jasmine async tests capabilities and requirejs.undef method for "clearing" a module (which will reset the loader's internal state to forget about the previous definition of the module, as it is stated in the docs).
This method has one implication, that it will not reset other modules, which are already loaded and depend on the reset module, but this should not be a problem, if one writes his tests in a right way, that is he tests only one module (and may be some parent ones, from which the child inherits some behavior), so that it should not be hard to undef
several modules at ones.
describe('Some/Module tests', function() {
'use strict'
var M
beforeEach(function(done) {
requirejs.undef('src/Some/GParentModule')
requirejs.undef('src/Some/ParentModule')
requirejs.undef('src/Some/Module')
require(['src/Some/Module'], function(m) {
M = m
done()
})
})
it('some description', function() {
var i = 0
for (var prop in M) {
i++
}
expect(i).toEqual(1)
expect(prop).toEqual('init')
expect(M.init).toEqual(jasmine.any(Function))
})
})
I've checked this, between it
calls changes to the module do not persist, so I suppose, it's safe to use this approach.
Thanks everyone for help, if someone has a better way, please answer here.
- is there any way to "reset" (clear) module in requirejs?
You can use requirejs.undef()
, but the documentation mention some gotchas. And to my knowledge there is no call you can use to say "unload this module and everything it depends on", which in a complex application is probably what is needed.
- is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
The better approach is to design your application so that state is tied to an object you instantiate rather than to the module itself. This is what I do in the test suite for a large application of mine. The beforeEach
hook resets what requires resetting and instantiates the application anew for each test.
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