AJAX Travelogue (Part 6): Mocking in JavaScript

By johanneslink

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)

Tags: , , , ,

6 Responses to “AJAX Travelogue (Part 6): Mocking in JavaScript”

  1. AJAX Travelogue (Part 7): MockMe « My Not So Private Tech Life Says:

    [...] My Not So Private Tech Life Johannes Link’s Travels through Software Devlopment Space « AJAX Travelogue (Part 6): Mocking in JavaScript [...]

  2. Ajaxian » MockMe: A new JavaScript mocking framework Says:

    [...] is an Agile fellow who wasn’t 100% happy with the existing JavaScript unit test frameworks, and he explained why. He gives an example: PLAIN TEXT [...]

  3. Ajax Girl » Blog Archive » MockMe: A new JavaScript mocking framework Says:

    [...] Agile fellow who wasn’t 100% happy with the existing JavaScript unit test frameworks, and he explained why. He gives an example: PLAIN TEXT [...]

  4. Javascript News » Blog Archive » MockMe: A new JavaScript mocking framework Says:

    [...] Agile fellow who wasn’t 100% happy with the existing JavaScript unit test frameworks, and he explained why. He gives an example: PLAIN TEXT [...]

  5. Stefan Roock Says:

    Hi,
    in my opinion your approach is a bit odd. You try to avoid modifications to the existing code. TDD in Java leads to interfaces you need for the test. The introduction of these interfaces is seen as design enhancement.

    Therefore I would modify the Java-Script-Code to make it mockable more easily. And that would mean to inject a Speaker object to the DoubleSpeaker object. Then you don’t have to clean up the mess at the end of your test.

    Having said that: A mock framework may be useful although. You may have to write too much test code even when you modify the testet code. But I haven’t an example at hand.

Leave a Reply