JsMockitoとかJSMockとか色々あるようですが、変にDSLのようなものを覚えるのも面倒だったのでスパッとモックできるようにしてみました。
機能は任意の関数を、別の関数で一時的に置き換えるだけです。Ajaxを使うような関数のユニットテストなどがやりやすくなります。
// Mock.makeで関数を置き換え。
// 最初の引数は関数名で、次は関数オブジェクト。
Mock.make("some_func", new_func);
// some_funcを呼ぶとnew_funcが実行される
some_func();
// Mock.revertで元に戻る
Mock.revert("some_func");
// Mock.revert_allで全てのモックが元に戻る
Mock.revert_all();
使い方の例(1)
// prompt()をモックしてみる
Mock.make("prompt", function(title, text){
return "あばびぶべー!";
});
var name = prompt("What's your name?")
console.log(name); //=> "あばびぶべー!"
Mock.revert("prompt"); // これでもとに戻った
使い方の例(2)
test("#save saves the text", function(){
var ajax_called = false;
// $.ajaxをモックする。
Mock.make("$.ajax", function(opts){
ajax_called = true;
// $.ajaxが呼ばれたときにオプションが適切かどうかをassertする
equals(opts.url, "/jsapi/pages/set_title");
equals(opts.type, "post");
equals(opts.data.id, page_id);
equals(opts.data.title, new_title);
// successコールバックを呼ぶ。
opts.success({success:true}, null, null);
});
// MyLibrary.saveは$.ajaxを呼ぶはず。
MyLibrary.save(25, "This is the new text.");
ok(ajax_called, "Ajax was called.");
});
本体
var Mock = {
store: {},
make: function(funcname, newfunc){
if (this.store[funcname]==undefined){
this.store[funcname] = (function(){
return eval(funcname);
}).apply(arguments.caller);
}
(function(){
eval(funcname+"=newfunc;");
}).apply(arguments.caller);
},
revert: function(funcname){
if(this.store[funcname]!=undefined){
var self = this;
(function(){
eval(funcname+"=self.store[funcname];");
}).apply(arguments.caller);
this.store[funcname] = undefined;
}
},
revert_all: function(){
for(var funcname in this.store){
this.revert(funcname);
}
},
};