I've been experimenting with QUnit tests and was looking for a good method to override functions with mocks so as to enable more atomic tests. There are good solutions out there for specific cases, for example overriding $.ajax (Simple jQuery (1.5+) AJAX Mocking), but I was looking for a more general approach and came up with this:
// 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 + "'];");
});
}
}
Which can then be used like:
module("Comments", {
setup: function () {
this.overrides = new overrides({ "$.ajax": function (url, options) {
alert("ajax: " + url + ', ' + options);
}
});
this.overrides.set();
},
teardown: function () {
this.overrides.reset();
}
});
Now, that all appears to work fine, and although this may not be the worst possible use of eval(), I was wondering if this could indeed be written without using eval()? I've read up on a bunch of the other eval() questions here and tried various options like accessing the overrides using window[] but that does not work for the $.ajax case for example (window['$'].ajax works but not window['$.ajax']).
Perhaps I'm also thinking to hard and eval() can be used safely here or there is a better approach in general for function overrides?
Why can't you just treat the objects as objects?
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;