Search code examples
javascriptmockingevalqunit

How to write this JavaScript code without eval (QUnit mocking)?


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?


Solution

  • 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;