Spying on jQuery $('...') selector in jasmine

12,008

Solution 1

Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.

Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.

function spyOn(obj, methodName) {
    var original = obj[methodName];
    var spy = jasmine.getEnv().spyOn(obj, methodName);
    spy.unspy = function () {
        if (original) {
            obj[methodName] = original;
            original = null;
        }
    };
    return spy;
}

describe("unspy", function () {
    it("removes the spy", function () {
        var mockDiv = document.createElement("div");
        var mockResult = $(mockDiv);

        spyOn(window, "$").and.returnValue(mockResult);
        expect($(document.body).get(0)).toBe(mockDiv);

        $.unspy();

        expect(jasmine.isSpy($)).toEqual(false);
        expect($(document.body).get(0)).toBe(document.body);
    });
});

As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.

// Original
myObj.doStuff = function () {
    $("#someElement").css("color", "red");
};

// Becomes...
myObj.doStuff = function () {
    this.getElements().css("color", "red");
};

myObj.getElements = function () {
    return $("#someElement");
};

// Test case
it("does stuff", function () {
    spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
    // ...
});

Solution 2

By spying on the window itself you have access to any window properties. As Jquery is one of these you can easily mock it as below and return the value you require.

spyOn(window, '$').and.returnValue(mockElement);

Or add a callFake with the input if it needs to be dynamic.

Share:
12,008

Related videos on Youtube

parxier
Author by

parxier

Updated on September 14, 2022

Comments

  • parxier
    parxier over 1 year

    When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:

    spyOn($.fn, "bind");
    

    The problem is when you want to spy on $('...') and return defined array of elements.

    Things tried after reading other related answers on SO:

    spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
    spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
    spyOn($.fn, "val").andReturn(elements); // function never gets called
    

    So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.

    jQuery version is 1.9.1.

    WORKAROUND:

    The only way I could make it work so far (ugly):

    realDollar = $;
    try {
      $ = jasmine.createSpy("dollar").andReturn(elements);
      // test code and asserts go here
    } finally {
      $ = realDollar;
    }
    
  • Erik A
    Erik A over 6 years
    This is a code-only answer. Code-only answers may be valid, but get automatically flagged as low quality. Please add a short explanation why this code works, or what the error was. -From review