我是单元测试的新手,所以我可能会缺少一些东西,但是我应该如何构造requirejs模块以使其完全可测试呢?考虑优雅的显示模块模式。

define([], function () {
    "use strict";

    var func1 = function(){
        var data = func2();
    };
    var func2 = function(){
        return db.call();
    };

    return {
        func1 : func1
    }
});

据我所知,这是构建requirejs模块的最常见模式。如果我错了,请纠正我!因此,在这种简单的情况下,由于它是全局的,因此我可以轻松测试func1的返回值和行为。但是,为了测试func2,我还必须返回它的引用。正确的?
return {
    func1 : func1,
    _test_func2 : func2
}

这样会使代码看起来不太漂亮,但总体来说还是可以的。但是,如果我想模拟func2并通过使用Jasmine spy替换其返回值,我将无法执行此操作,因为该方法位于闭包内部。

所以我的问题是如何构造requirejs模块以使其完全可测试?对于这种情况,是否有比揭示模块模式更好的模式?

最佳答案

如果模块中的函数直接调用该模块的其他函数(即通过使用模块本地的引用),则无法从外部拦截这些调用。但是,如果更改模块以使其内部的函数以与外部代码相同的方式调用模块的函数,则可以拦截这些调用。

这是一个允许您想要的示例:

define([], function () {
    "use strict";

    var foo = function(){
        return exports.bar();
    };

    var bar = function(){
        return "original";
    };

    var exports =  {
        foo: foo,
        bar: bar
    };

    return exports;
});

关键是foo会通过exports来访问bar而不是直接调用它。

我已经提出了一个可运行的示例herespec/main.spec.js文件包含:
    expect(moduleA.foo()).toEqual("original");

    spyOn(moduleA, "bar").andReturn("patched");

    expect(moduleA.foo()).toEqual("patched");

您会注意到bar是已修补的函数,但foo受修补影响。

另外,为了避免永久性地破坏导出代码,我有时会进行环境检查以确定该模块是否在测试环境中运行,并且仅在测试模式下导出测试所需的功能,而仅测试。这是我编写的实际代码示例:
var options = module.config();
var test = options && options.test;

[...]
// For testing only
if (test) {
    exports.__test = {
        $modal: $modal,
        reset: _reset,
        is_terminating: _is_terminating
    };
}

如果requirejs配置(使用 config )配置我的模块,以便将test选项设置为true值,则导出将另外包含__test符号,该符号包含我在测试模块时要导出的一些其他项。否则,这些符号将不可用。

编辑:如果困扰上述第一种方法的原因是必须使用exports为内部函数的所有调用添加前缀,则可以执行以下操作:
define(["module"], function (module) {
    "use strict";

    var debug = module.config().debug;
    var exports = {};

    /**
     * @function
     * @param {String} name Name of the function to export
     * @param {Function} f Function to export.
     * @returns {Function} A wrapper for <code>f</code>, or <code>f</code>.
     */
    var _dynamic = (debug ?
        function (name, f) {
            exports[name] = f;
            return function () {
                // This call allows for future changes to arguments passed..
                return exports[name].apply(this, arguments);
            };
        } :
        _dynamic = function (name, f) { return f; });

    var foo = function () {
        return bar(1, 2, 3);
    };

    var bar = _dynamic("bar", function (a, b, c) {
        return "original: called with " + a + " " + b + " " + c;
    });

    exports.foo = foo;

    return exports;
});

当RequireJS配置对上面的模块进行配置,使得debug为true时,它将导出_dynamic包装的函数,并提供允许在不通过exports的情况下引用它们的本地符号。如果debug为false,则不会导出该函数,也不会对其进行包装。我已经更新了example以显示此方法。在示例中是moduleB

09-30 16:11
查看更多