Spying on jQuery

1,436 views
Skip to first unread message

Eric

unread,
May 19, 2011, 10:36:40 AM5/19/11
to Jasmine
Hello,

My team at AppNexus recently adopted Jasmine for javascript testing.
I'm very excited by the product and the potential it has for improving
our development.

In some cases we've set up the jQuery shortcut ($) as a spy, like so:

$ = spyOn(window, '$');

This works fine for spying on calls such as:

$("#somediv")

But the spy creation destroys all of the functions defined on the $
object. So when I create my spy I lose $.extend, $.ajax, $.each, etc.
I end up having to wrap the spy creation in a function where I
preserve the properties of the $ object.

function spyOn$() {
var spy = spyOn(window, '$');

for (var i in jQuery) {
if (jQuery.hasOwnProperty(i)) $[i] = jQuery[i];
}

return spy;
}

Anyone have a better solution? I've considered modifying our fork of
jasmine to make spy creation less destructive on JavaScript function
properties. But would love to know if there's other approaches folks
have adopted.

Thanks,

Eric Anderson

Rajan Agaskar

unread,
May 19, 2011, 1:25:54 PM5/19/11
to jasmi...@googlegroups.com
We should probably copy properties when spying (I think this might actually be a story in our backlog), that said, I feel like it's unusual requirement to spy on the jquery object directly. I can't think of few cases where I've actually needed to do that, and it's often a sign that your tests are a little too closely tied to implementation. More often I will either: 1) let the code execute and then look at the DOM to ensure what I expected actually occurred, or 2) spy on the particular jquery property I'm calling, like spyOn($, 'ajax'), or spyOn($.fn, "someJqueryFn"). If it helps, mostRecentCall returns an array that includes both the arguments (args) and the scope (object) -- because jQuery always passes the object as the scope to fn functions, you can then check the mostRecentCall.object against your expected.

Unfortunately, if you really do need to spyOn(window, "$"), I can't think of anything better than copying properties.




--
You received this message because you are subscribed to the Google Groups "Jasmine" group.
To post to this group, send email to jasmi...@googlegroups.com.
To unsubscribe from this group, send email to jasmine-js+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/jasmine-js?hl=en.


Eric Anderson

unread,
May 19, 2011, 2:03:39 PM5/19/11
to jasmi...@googlegroups.com
Rajan,

Thanks for the info. In reality I don't think we'll be spying directly on jquery very often. But more often will be doing spyOn($, 'ajax') as you mention.

I think the main utility is in accessing code that is protected by closure.

So, if I have some jquery selection that is protected by closure from being injected with a spy directly, and is part of a large code execution block (like a 'DOM-ready' style event), spying on jquery can be useful to isolate a single assertion in the midst of a large action.

For instance, in this example a layout is applied to a portion of the window. But perhaps we want to inject a spy for one of the jquery selections in the executing code.

var layoutDiv = $('#layout-div');
layoutDivSpy = spyOn(layoutDiv, 'renderLayout').andCallFake(function() {});

$ = spyOn$().andCallFake(function(selector) {
    switch (selector) {
        case "#layout-div":
              return layoutDivSpy;
        break;
        default:
              return jQuery(selector);
    }
});

initScreenLoad(); // SUT

expect(layoutDivSpy.renderLayout).toHaveBeenCalled();

This may be too tight a coupling between the test and the system code, but that's the type of operation I think could be useful.

Thanks,

Eric
Reply all
Reply to author
Forward
0 new messages