我有一个AMD模块,我想测试,但我想模拟出的不是加载实际依赖它的依赖。 我使用requirejs,为我的模块的代码看起来是这样的:
define(['hurp', 'durp'], function(Hurp, Durp) {
return {
foo: function () {
console.log(Hurp.beans)
},
bar: function () {
console.log(Durp.beans)
}
}
}
我怎样才能模拟出hurp
和durp
这样我就可以有效的单元测试?
Answer 1:
所以看完这篇文章我想出了使用requirejs配置功能来创建测试新的上下文,你可以简单地嘲笑你的依赖的解决方案:
var cnt = 0;
function createContext(stubs) {
cnt++;
var map = {};
var i18n = stubs.i18n;
stubs.i18n = {
load: sinon.spy(function(name, req, onLoad) {
onLoad(i18n);
})
};
_.each(stubs, function(value, key) {
var stubName = 'stub' + key + cnt;
map[key] = stubName;
define(stubName, function() {
return value;
});
});
return require.config({
context: "context_" + cnt,
map: {
"*": map
},
baseUrl: 'js/cfe/app/'
});
}
因此,它会创建一个新的上下文,其中用于定义Hurp
和Durp
将由您传递给函数的对象设置。 该名的Math.random也许是有点脏,但它的工作原理。 因为如果你有一大堆的测试,你需要创建新的上下文,每间套房,以防止重用你的嘲笑,或当你想真正的requirejs模块加载嘲笑。
在你的情况是这样的:
(function () {
var stubs = {
hurp: 'hurp',
durp: 'durp'
};
var context = createContext(stubs);
context(['yourModuleName'], function (yourModule) {
//your normal jasmine test starts here
describe("yourModuleName", function () {
it('should log', function(){
spyOn(console, 'log');
yourModule.foo();
expect(console.log).toHasBeenCalledWith('hurp');
})
});
});
})();
所以我使用了一段时间,其真正强大的生产中这种做法。
Answer 2:
你可能想看看新的Squire.js LIB
从文档:
Squire.js是依赖注入的Require.js用户做出嘲讽的依赖容易!
Answer 3:
我发现这个问题,有三种不同的解决方案,他们都不愉快。
定义依赖在线
define('hurp', [], function () {
return {
beans: 'Beans'
};
});
define('durp', [], function () {
return {
beans: 'durp beans'
};
});
require('hurpdhurp', function () {
// test hurpdurp in here
});
的fugly。 你必须弄乱你的测试有很多AMD的样板。
加载模拟依赖从不同路径
这涉及使用单独的config.js文件来定义每个指向嘲笑而不是原来的依赖关系的依赖关系的路径。 这也是丑陋的,需要吨的测试文件和配置文件的创建。
假货在节点
这是我目前的解决方案,但仍然是一个可怕的一个。
您可以创建自己define
函数来提供自己的嘲弄到模块,并把您的测试回调。 然后你eval
模块运行测试,如下所示:
var fs = require('fs')
, hurp = {
beans: 'BEANS'
}
, durp = {
beans: 'durp beans'
}
, hurpDurp = fs.readFileSync('path/to/hurpDurp', 'utf8');
;
function define(deps, cb) {
var TestableHurpDurp = cb(hurp, durp);
// now run tests below on TestableHurpDurp, which is using your
// passed-in mocks as dependencies.
}
// evaluate the AMD module, running your mocked define function and your tests.
eval(hurpDurp);
这是我的首选解决方案。 它看起来有点神奇,但它有几个好处。
- 运行节点的测试,所以用浏览器自动化无需复杂。
- 不太需要在测试中凌乱AMD样板。
- 你能使用
eval
愤怒,并想象克罗克福德愤怒爆炸。
它仍然有一些缺点,很明显。
- 既然你是在节点测试,你不能做与浏览器事件或DOM操作任何东西。 不仅有利于测试逻辑。
- 还是有点笨重成立。 你需要模拟出
define
在每个测试,因为这是你的实际测试运行。
我工作的一个测试运行,给出这种东西用优雅的语法,但我仍然有问题1没有很好的解决方案。
结论
在requirejs惩戒DEPS吸硬。 我发现了一个方式,sortof的作品,但我仍然不是很满意。 请让我知道,如果你有更好的想法。
Answer 4:
有一个config.map
选项http://requirejs.org/docs/api.html#config-map 。
关于如何对使用它:
- 定义正常模块;
- 定义桩模块;
配置RequireJS expicitely;
requirejs.config({ map: { 'source/js': { 'foo': 'normalModule' }, 'source/test': { 'foo': 'stubModule' } } });
在这种情况下,正常和测试代码,你可以使用foo
模块,这将是真正的模块引用和相应的存根。
Answer 5:
您可以使用testr.js嘲笑的依赖。 您可以设置testr加载模拟的依赖,而不是原来的。 下面是一个例子用法:
var fakeDep = function(){
this.getText = function(){
return 'Fake Dependancy';
};
};
var Module1 = testr('module1', {
'dependancies/dependancy1':fakeDep
});
看看这个还有: http://cyberasylum.janithw.com/mocking-requirejs-dependencies-for-unit-testing/
Answer 6:
这个答案是基于安德烈亚斯Köberle的答案 。
这是不是很简单,我实现和理解他的解决方案,所以它是如何工作的,我会更详细一点解释,以及一些需要避免的陷阱,希望这将有助于未来的游客。
所以,首先设置的:
我使用的是噶作为测试运行和MochaJs作为测试框架。
使用类似的乡绅 ,我没有工作,出于某种原因,当我用它,测试框架扔错误:
类型错误:无法读取属性“叫”未定义
RequireJs有可能映射模块ID到其他模块ID。 它还允许创建一个require
的功能 ,它使用一个不同的配置超过了全球require
。
这些功能是至关重要的这个解决方案的工作。
这里是我的版本的模拟代码,包括(很多)评论(我希望它可以理解的)。 我把它包在模块内,这样的测试可以很容易地需要它。
define([], function () {
var count = 0;
var requireJsMock= Object.create(null);
requireJsMock.createMockRequire = function (mocks) {
//mocks is an object with the module ids/paths as keys, and the module as value
count++;
var map = {};
//register the mocks with unique names, and create a mapping from the mocked module id to the mock module id
//this will cause RequireJs to load the mock module instead of the real one
for (property in mocks) {
if (mocks.hasOwnProperty(property)) {
var moduleId = property; //the object property is the module id
var module = mocks[property]; //the value is the mock
var stubId = 'stub' + moduleId + count; //create a unique name to register the module
map[moduleId] = stubId; //add to the mapping
//register the mock with the unique id, so that RequireJs can actually call it
define(stubId, function () {
return module;
});
}
}
var defaultContext = requirejs.s.contexts._.config;
var requireMockContext = { baseUrl: defaultContext.baseUrl }; //use the baseUrl of the global RequireJs config, so that it doesn't have to be repeated here
requireMockContext.context = "context_" + count; //use a unique context name, so that the configs dont overlap
//use the mapping for all modules
requireMockContext.map = {
"*": map
};
return require.config(requireMockContext); //create a require function that uses the new config
};
return requireJsMock;
});
我遇到的最大缺陷在于 ,它的字面花了我小时,创造了RequireJs配置。 我试过(深)复制,并且只覆盖必要的属性(如上下文或地图)。 这不起作用! 只复制baseUrl
,这工作正常。
用法
要使用它,需要它在你的测试中,创造了嘲笑,然后把它传递给createMockRequire
。 例如:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
在这里, 一个完整的测试文件的例子 :
define(["chai", "requireJsMock"], function (chai, requireJsMock) {
var expect = chai.expect;
describe("Module", function () {
describe("Method", function () {
it("should work", function () {
return new Promise(function (resolve, reject) {
var handler = { handle: function () { } };
var called = 0;
var moduleBMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleBIdOrPath": moduleBMock
}
var requireMocks = requireJsMock.createMockRequire(mocks);
requireMocks(["js/ModuleA"], function (moduleA) {
try {
moduleA.method(); //moduleA should call method of moduleBMock
expect(called).to.equal(1);
resolve();
} catch (e) {
reject(e);
}
});
});
});
});
});
});
Answer 7:
如果你想使该隔离一个单元有普通的JS测试,那么你可以简单地使用这个片段:
function define(args, func){
if(!args.length){
throw new Error("please stick to the require.js api which wants a: define(['mydependency'], function(){})");
}
var fileName = document.scripts[document.scripts.length-1].src;
// get rid of the url and path elements
fileName = fileName.split("/");
fileName = fileName[fileName.length-1];
// get rid of the file ending
fileName = fileName.split(".");
fileName = fileName[0];
window[fileName] = func;
return func;
}
window.define = define;
文章来源: How can I mock dependencies for unit testing in RequireJS?