Published: August 08 2008

It's been a while since I have written about AJAX development; I published Part  5 over two years ago. Why's that, you ask? Well, at a given time I was either too busy actually doing AJAX stuff or - at other times - my focus had shifted away. It wasn't before my talk about Test-Driven Ajax had been accepted for Agile 2008 that I finally took my time and rethought some of the things I had learned about the topic before.

Let's set up the stage for part 6 of this show, which will deal with mocking in JavaScript. As a prerequisite I assume that you are using some kind of unit testing framework for JavaScript, like JsUnit or JsUnit (well, there exist two having this name) or - like I do - the framework coming with script.aculo.us.

Effective unit testing & especially test-driven development require that you test your units (think "functions" or "objects") in isolation. That's how stubbing & mocking come into the picture. Whereas mocking frameworks play a significant role in statically typed languages like Java or C#, some claim that dynamic languages don't really need that kind of thing because the mere presence of closures and duck typing make mocking and stubbing by hand so easy. JavaScript has both - closures (aka functions) and duck typing, so let's see how things turn out in a simple example. Consider the following two objects:

  var Speaker = {
    say: function(msg) {
      alert(msg);
    }
  };
  var DoubleSpeaker = {
    say: function(msg) {
      Speaker.say(msg+msg);
    }
  };

I would like to write a test for DoubleSpeaker.say to verify that Speaker.say is being called with the argument duobled. Since using the real Speaker object would result in an alert box, which is not suitable for an automated test, I have to find a way to substitute Speaker.say with some kind of mock function:

  testDoubleSpeaker: function() { with(this) {
    var actualMsg = null;
    var mockSay = function(msg) {
      actualMsg = msg;
    };
    Speaker.say = mockSay;
    DoubleSpeaker.say('oops');
    assertEqual('oopsoops', actualMsg);
  }}

This approach actually works, but it has a drawback. Since Speaker is a global object our test has changed the global state and other tests - those that want to use the real Speaker.say function - might fail because of our test. This sort of test interdependency is one of the biggest smells of unit testing, so we have to get rid of that stink and reset the global state on finishing:

  testDoubleSpeaker: function() { with(this) {
    var actualMsg = null;
    var mockSay = function(msg) {
      actualMsg = msg;
    };
    vor originalSay = Speaker.say;
    Speaker.say = mockSay;
    try {
      DoubleSpeaker.say('oops');
      assertEqual('oopsoops', actualMsg);
    } finally {
      Speaker.say = originalSay;
    }
  }}

To me that code looks an awfully lot like my very early mocking attempts with Java around 2001. The code is bloated and full of ceremony. The essence of the test could be written down in three lines:

  testDoubleSpeaker: function() { with(this) {
    makeAMockOf(Speaker.say);
    DoubleSpeaker.say('oops');
    checkThat(Speaker.say).wasInvokedWith('oopsoops');
  }}

I've intentionally chosen rather wordy lines to make it clear that this is not a real API - not that I'm aware of.

My personal take-away from even a simple example like the one above is two-fold:

  • Even a language like JavaScript can make good use of a framework to take the tedium away from mocking and stubbing.
  • Such a framework must be different than mock frameworks in other languages to compensate for the difference in programming idioms (e.g. the paramount use of global objects) and the fact that the basic building block in JavaScript is NOT the object but the function.

Luckily, you don't have to write such a beast yourself, it is already out there... (to be continued)

blog comments powered by Disqus