The RunCodeRun Blog

Sep 02 2009

Blue Ridge Pro Tip: Trust the Wiring, Test the Behavior

A Common Problem: Testing jQuery Event Handling

The prevailing style in jQuery is to inline your event handling like:

$(function(){
  $("#some-id").click(function{
    $(this).attr("some-attribute", "some new value");
  });
});

And so, most new users of Screw.Unit (and Blue Ridge) try to write specs like:

//do NOT do this!
it("does something", function(){
  // setup HTML fixture here

  $('#some-id').click();
  expect($('#some-id').attr("some-attribute")).to(equal, "some new value");
});

However, manually invoking the event usually doesn’t work as expected. It often doesn’t work at all. (More on that later.) Most important, though, is that this inline style is not reusable and is difficult to test.

The Solution: Trust the Wiring, Test the Behavior

Because I trust jQuery to handle events properly, I don’t test “wiring” code like the click() function. Instead I do something like this:

var SomeNamespace = {
  doSomething: function(element){
    $(element).attr("some-attribute", "some new value");
  }
};

$(function(){
  $("#some-id").click(function(){ SomeNamespace.doSomething(this) });
});

Now the spec can test the doSomething() function directly. I’d write a spec for doSomething() like:

it("does something", function(){
  // setup HTML fixture here

  SomeNamespace.doSomething("#some-id");
  expect($('#some-id').attr("some-attribute")).to(equal, "some new value");
});

Why Is This Better?

Clearer Intent

The code’s intent is clearer because it separates application-level logic from the event wiring. We focus on the essential logic, and we trust jQuery to do the rest.

Increased Reusability

The doSomething() function is more reusable because it can now work on any element. Note that the #some-id is hard-coded only in the wiring setup, not the function itself. Also, because we’re calling $(element), this function could be passed a selector string, a DOM element, or a jQuery object (that could potentially represent dozens of DOM elements). This style is very jQuery-idiomatic.

Greater Testability

Because the function is now properly named, you don’t have to go through any DOM/event contortions to test it. Also, you can set up several specs with different “fixtures” each highlighting a different aspect of the behavior under test.

Extra Credit: Why doesn’t the click() work?

Okay, technically it does work — if you get the timing of the event setup correctly. Which most folks don’t. It’s non-trivial, and so most developers either unnecessarily use the live() function as a crutch, or, in Blue Ridge, fall back to using the HTML fixture file which is undesirable.

Extra Extra Credit: Setting up HTML fixtures?

In a future blog entry I’ll cover setting up Factory Girl-style HTML fixtures inside your tests. For a few quick examples, though, check out the spec files and spec_helper.js file in the Blue Ridge sample app.

The Big Finish

Manually invoking events is one of the biggest problems new Screw.Unit and Blue Ridge testers face, and they just don’t have to. It’s unnecessary complexity. Trust that jQuery will do the right thing and get the event to your application function, and then all you need to do is test your own JavaScript behavior.

Comments (View)
Page 1 of 1
blog comments powered by Disqus