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: ajax, javascript, mock, MockMe, stub
August 9, 2008 at 15:02
[...] My Not So Private Tech Life Johannes Link’s Travels through Software Devlopment Space « AJAX Travelogue (Part 6): Mocking in JavaScript [...]
August 11, 2008 at 14:22
[...] 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 [...]
August 11, 2008 at 15:45
[...] Agile fellow who wasn’t 100% happy with the existing JavaScript unit test frameworks, and he explained why. He gives an example: PLAIN TEXT [...]
August 11, 2008 at 21:06
[...] Agile fellow who wasn’t 100% happy with the existing JavaScript unit test frameworks, and he explained why. He gives an example: PLAIN TEXT [...]
December 16, 2008 at 10:22
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.
December 16, 2008 at 10:48
Stefan, you’re perfectly right in that I’d do the similar thing differently in Java. IMO the class-based and statically typed nature of Java requires that to have good decoupled code.
In JavaScript, however, you don’t have to do as much explicit decoupling to arrive at a flexible design. Consider the global property ‘Speaker’ to be the hook for plugging in your actual speaker implementation; I think of this as implicit decoupling.
That said, I do think that most JavaScript code out there could arguably need more explicit decoupling and explicit dependencies. My example tried to mimic the way in which many of the existing libraries are coded. So, maybe you are right and I shouldn’t fall into JavaScript’s everything-is-public-trap.