[英]How to stub require() / expect calls to the “root” function of a module?
請考慮以下茉莉花規格:
describe("something.act()", function() {
it("calls some function of my module", function() {
var mod = require('my_module');
spyOn(mod, "someFunction");
something.act();
expect(mod.someFunction).toHaveBeenCalled();
});
});
這工作得非常好。 像這樣的東西使它變綠:
something.act = function() { require('my_module').someFunction(); };
現在來看看這個:
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = require('my_module');
spyOn(mod); // jasmine needs a property name
// pointing to a function as param #2
// therefore, this call is not correct.
something.act();
expect(mod).toHaveBeenCalled(); // mod should be a spy
});
});
這是我想用此規范測試的代碼:
something.act = function() { require('my_module')(); };
在過去的幾個月里,這讓我陷入困境幾次。 一個理論解決方案是替換require()並返回使用createSpy()創建的間諜。 但是require()是一個不可阻擋的野獸:它是每個源文件/模塊中函數的不同“副本”。 在規范中對它進行存根不會取代“testee”源文件中的真實require()函數。
另一種方法是在加載路徑中添加一些假模塊,但它看起來太復雜了。
任何的想法?
重新布線非常棒
var rewire = require('rewire');
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = rewire('my_module');
var mockRootFunction = jasmine.createSpy('mockRootFunction');
var requireSpy = {
mockRequire: function() {
return mockRootFunction;
}
};
spyOn(requireSpy, 'mockRequire').andCallThrough();
origRequire = mod.__get__('require');
mod.__set__('require', requireSpy.mockRequire);
something.act();
expect(requireSpy.mockRequire).toHaveBeenCalledWith('my_module');
expect(mockRootFunction).toHaveBeenCalled();
mod.__set__('require', origRequire);
});
});
看起來我找到了一個可接受的解決方案。
規范助手:
var moduleSpies = {};
var originalJsLoader = require.extensions['.js'];
spyOnModule = function spyOnModule(module) {
var path = require.resolve(module);
var spy = createSpy("spy on module \"" + module + "\"");
moduleSpies[path] = spy;
delete require.cache[path];
return spy;
};
require.extensions['.js'] = function (obj, path) {
if (moduleSpies[path])
obj.exports = moduleSpies[path];
else
return originalJsLoader(obj, path);
}
afterEach(function() {
for (var path in moduleSpies) {
delete moduleSpies[path];
}
});
規格:
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = spyOnModule('my_module');
something.act();
expect(mod).toHaveBeenCalled(); // mod is a spy
});
});
這並不完美,但工作做得很好。 它甚至沒有考慮到被測試者的源代碼,這對我來說是一種標准。
我今天需要這樣做,並發現了這篇文章。 我的解決方案是:
在規范助手中:
var originalRequire = require;
var requireOverrides = {};
stubModule = function(name) {
var double = originalRequire(name);
double['double'] = name;
requireOverrides[name] = double;
return double;
}
require = function(name) {
if (requireOverrides[name]) {
return requireOverrides[name];
} else {
return originalRequire(name);
}
}
afterEach(function() {
requireOverrides = {};
});
在一個規范中:
AWS = stubModule('aws-sdk');
spyOn(AWS.S3, 'Client');
// do something
expect(AWS.S3.Client).toHaveBeenCalled();
這非常有用,但它不支持通過via .andCallThrough()
調用。
我能夠適應它,所以我想我會分享:
function clone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
var key;
var temp = new obj.constructor();
for (key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = clone(obj[key]);
}
}
return temp;
};
spyOnModule = function spyOnModule(name) {
var path = require.resolve(name);
var spy = createSpy("spy on module \"" + name + "\"");
moduleSpies[path] = spy;
// Fake calling through
spy.andCallThrough = function() {
// Create a module object
var mod = clone(module);
mod.parent = module;
mod.id = path;
mod.filename = path;
// Load it backdoor
originalJsLoader(mod, path);
// And set it's export as a faked call
return this.andCallFake(mod.exports);
}
delete require.cache[path];
return spy;
};
您可以輕輕使用模塊(https://github.com/felixge/node-gently)。 在示例中提到了劫持要求,並且臟NPM模塊主動使用它,因此我認為它有效。
還有另一種方法。 您可以在需要時不使用var
將模塊放在全局范圍中:
someModule = require('someModule');
describe('whatever', function() {
it('does something', function() {
spyOn(global, 'someModule');
someFunctionThatShouldCallTheModule();
expect(someModule).toHaveBeenCalled();
}
}
您也可以將模塊包裝在另一個模塊中:
//someModuleWrapper.js
require('someModule');
function callModule(arg) {
someModule(arg);
}
exports.callModule = callModule;
//In the spec file:
someModuleWrapper = require('someModuleWrapper');
describe('whatever', function() {
it('does something', function() {
spyOn(someModuleWrapper, 'callModule');
someFunctionThatShouldCallTheModule();
expect(someModuleWrapper.callModule).toHaveBeenCalled();
}
}
然后顯然要確保無論someFunctionThatShouldCallTheModule
是什么,你需要包裝而不是真正的模塊。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.