我是单元测试的新手,所以我可能会遗漏一些东西,但我应该如何构建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 }
这使得代码稍微不那么漂亮,但整体仍然可以.但是,如果我想通过使用Jasmine间谍来模拟func2并替换它的返回值,那么我将无法将该方法置于闭包内.
所以我的问题是如何构建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通过导出到访问栏而不是直接调用它.
我已经提出了一个可运行的示例here. spec / 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
),以便它将测试选项设置为true值,那么导出将另外包含一个__test符号,该符号包含我在测试模块时要导出的一些其他项目.否则,这些符号不可用.
编辑:如果上面的第一个方法困扰你的是必须使用导出为所有内部函数调用前缀,你可以这样做:
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包装的函数,并提供允许引用它们而不经过导出的本地符号.如果debug为false,则不会导出该函数,也不会将其包装.我已更新example以显示此方法.它是示例中的moduleB.