user907860 user907860 - 21 days ago 4
Javascript Question

how to reset a requirejs module between unit tests

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:


  • is there any way to "reset" (clear) module in requirejs? (actually, I suppose that there is no such a way, except reload all modules once again)

  • 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 ?


Answer

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.

Comments