简体   繁体   中英

How to inject module in test for requirejs to unit test application?

I have two modules, ModuleA and ModuleB. ModuleB depends on ModuleA. But as far as I know requirejs is kind of service locator. So how do I unit test this kind of pattern in my code.

define('moduleA', function() {
    var ModuleA = function() {
        this.doSomething = function() {
            return true;
        }
    }
    return ModuleA;
});

define('moduleB', ['moduleA'], function(ModuleA) {
    var moduleA = new ModuleA();
    var ModuleB = function() {
        this.doSomethingElse = function() {
            if(moduleA.doSomething()) {
                // do something else and return

            } else {
                // do other things and return
            }
        }
    }

    return ModuleB
});

/*Test for moduleB*/
define('test', ['moduleB', function(ModuleB) {
    describe('', function() {
        var moduleB = new ModuleB();
        expect(moduleB.doSomethingElse()).toBe(true);
    });
});

Because ModuleA will be initialised all the time. Is there anyway I could inject some mock to ModuleB and test that? And I would like to not use singleton because I'm not sure if the test will keep failing because of unforeseen behaviour.

Note: I just type the code so their might be some compilation error.

This code pattern is not unit testable. I guess someone could come up with a way of tricking RequireJS to load different module definition and unload it at the end of the test but that would be too complicated and too error prone, and wouldn't solve the problem of bad coding practice.

The pattern is not testable because inside of moduleB definition you are creating new instance of moduleA (plus loading of ModuleA is "hardcoded"). To make it testable you need to inject it. Consider something like:

define('moduleB', function() {
    var ModuleB = function(moduleA) {
        this.__moduleA = moduleA;
        this.doSomethingElse = function() {
            if(this.__moduleA.doSomething()) {
                // do something else and return

            } else {
                // do other things and return
            }
        }
    }

    return ModuleB
});

To further explore this small refactoring I would suggest reading about "dependency injection". Skip DI tools like Guice, just get to the core of DI idea.

Misko Hevery has couple interesting posts about this kind of OOP patterns including how to deal with "new" keyword: http://misko.hevery.com/2008/09/10/where-have-all-the-new-operators-gone/

I can not find it right now, but I believe Misko had nice post about 3 hierarchies in OOP, inheritance hierarchy (most obvious), creation hierarchy (who creates what) and dependency hierarchy (who uses whom). These 3 hierarchies are completely different. Usually if class B creates class A it shouldn't use it. The role of class B seems to be of a factory - it creates instances. There should be some other class C that is using injected instance of class A to perform some business logic.

As a side note, since you are already using AMD modules and JS "classes" why not to make your classes/object look and behave more like proper OOP classes/object? Dejavu can help you with that: http://wojciechszela.com/blog/2014/01/13/object-oriented-programming-in-javascript/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM