テスト対象関数でrequire()
が使われていて,どうにかして置き換えたい時があります
本来ならtestし易いように関数の構成を変更してstubを使えばいいと思いますが,
諸事情あって中のソースは変更しないでテストをする ということをしました.
方法としてはrequire
をフックする ということをします
書いたコード
var moduleStubs = {};
var originalJsLoader = require.extensions['.js'];
var stubOnModule = function(module) {
var path = require.resolve(module);
var stub = sinon.stub();
moduleStubs[path] = stub;
delete require.cache[path];
return stub;
};
require.extensions['.js'] = function (obj, path) {
if (moduleStubs[path])
obj.exports = moduleStubs[path];
else
return originalJsLoader(obj, path);
};
afterEach(function() {
for (var path in moduleStubs) {
delete moduleStubs[path];
}
});
requireモジュールの中身にあまり詳しくないですが,どうやらrequire.cache[path]
に
キャッシュがあるようなのでそれを削除し,
requireでjsファイルが呼ばれたらキャッシュしてあったstubを返すようにしています
afterEach
に関してはmochaでテスト完了後に毎回stubを直す処理を書いています,.
使い方
stubOnModule(“ws”);
とテストの最初にかけば,その後呼ばれるrequire(‘ws’)はstubに置き換わります.
あくまでstubへの置き換えだけなので,new や 下位pathへのアクセスをされるとプロパティがなくてエラーになります
it('test', function(){
stubOnModule("module");
var m = require("module"); //return stub
var instance = new m(); // OK
var value = m.b; // ok
m.a(); // error : m.a is not a function
var value = m.c.d; // error : Cannot read property 'd' of undefined
var instance2 = new m.A({}); // error : ws.Server is not a constructor
});
その場合は,一つ一つstubにしてあげましょう
it('test', function(){
var stub = stubOnModule("module");
stub.a = sinon.stub();
stub.c = sinon.stub();
stub.c.d = sinon.stub();
stub.A = sinon.stub();
var m = require("module"); //return stub
m.a(); // error : m.a is not a function
var value = m.c.d; // error : Cannot read property 'd' of undefined
var instance2 = new m.A({}); // error : m.A is not a constructor
});
参考