我一直在尝试使用QUnit测试,并且正在寻找一种用mocks覆盖函数的好方法,以便能够进行更多的原子测试。对于特定情况,有很好的解决方案,例如覆盖$ .ajax(Simple jQuery (1.5+) AJAX Mocking),但我正在寻找更通用的方法,并想出了这个:
// Constructor for overrides.
function overrides(overrides) {
this.overrides = overrides;
}
// Implementation for overrides.
overrides.prototype = {
set: function () {
var functions = {};
$.each(this.overrides, function (key, value) {
eval("functions['" + key + "'] = " + key + ";");
eval(key + " = value;");
});
this.functions = functions;
},
reset: function () {
var functions = this.functions;
$.each(this.overrides, function (key, _) {
eval(key + " = functions['" + key + "'];");
});
}
}
然后可以使用:
module("Comments", {
setup: function () {
this.overrides = new overrides({ "$.ajax": function (url, options) {
alert("ajax: " + url + ', ' + options);
}
});
this.overrides.set();
},
teardown: function () {
this.overrides.reset();
}
});
现在,一切似乎都运行正常,虽然这可能不是eval()的最坏使用,但我想知道这是否确实可以在不使用eval()的情况下编写?我在这里阅读了一堆其他的eval()问题并尝试了各种选项,比如使用window []访问覆盖,但这对$ .ajax情况不起作用(例如window ['$']。ajax有效但不是窗口['$ .ajax'])。
也许我也在努力思考,eval()可以在这里安全地使用,或者对于函数覆盖有更好的方法吗?
答案 0 :(得分:2)
为什么不能将对象视为对象?
functions[key] = key;
var arr = key.split('.');
var obj = window;
for (var i = 0; i < arr.length; i++){
if (obj) obj = obj[arr[i]];
}
obj = value;
答案 1 :(得分:1)
据我所知,唯一的方法是分别提供对象和属性名称。本地函数(例如Object.defineProperty
)也是这种情况,它将对象作为一个参数,将属性名称作为字符串作为另一个参数。
// overwrite properties inside `$`
new overrides($, {"ajax": function (url, options) {
alert("ajax: " + url + ', ' + options);
}});
这样的事情:
function overrides(obj, overrides) {
this.obj = obj;
this.overrides = overrides;
}
和
set: function () {
var functions = {};
var inst = this;
$.each(this.overrides, function (key, value) {
functions[key] = inst.obj[key]; // old function
inst.obj[key] = value; // overwrite function
});
this.functions = functions;
},
它有效,因为inst.obj
和$
引用相同的对象 - inst.obj
上的更改属性也会修改$
。