JavaScript Test Driven Development with JsUnit and JsMock

 

 

This article is a crash course in writing maintainable JavaScript. We'll add features to a running example by iteratively following a simple principle: write a unit test, make it pass. Each test will serve as a quality feedback loop, creating both a safety net and an executable form of documentation for anyone who wants to change the production code. By starting each feature with a simple failing test we ensure that all features are tested. We avoid the cost of rewriting code to test it later. This is particularly valuable given the fact that JavaScript developers have so much rope to hang themselves with - consider how much global mutable state there is between the DOM API and the language itself.

Our running example is a casino slot machine with three reels.† Each reel has five possible states, represented by images.† Each reel randomly assumes a state when the play button of the slot machine is pressed.†The slot machine balance will be incremented or decremented depending on whether or not all three reel states are equal.

Our tools will be stubs, mock objects and a little bit of dependency injection.†We will use JsUnit to run unit tests and a JavaScript mock object library called JsMock. Integration testing, a compliment of unit testing, is beyond the scope of this article. This does not mean integration testing is less important - just that we will strive for quick feedback rather than the slower and more comprehensive feedback obtained with tools like Selenium and Watir.

slot_machine.bmp

JsUnit, a JavaScript Unit Testing Framework

JsUnit is an open source unit testing framework for JavaScript. It is inspired by JUnit and written entirely in JavaScript. In addition to being the most popular JavaScript unit testing framework it comes with a set of utilities used to run test suites in continuous builds via ant tasks, JUnit or a command line.

Letís start with the JsUnit test runner.† The test runner is a plain HTML and JavaScript web page, meaning that your unit tests will be running directly in the browser or browsers you wish to support.† Unzip the JsUnit download file and you will find testRunner.html in the root directory.† You do not need to serve the test runner from a web server Ė simply load it by browsing to it via the file system.

The most important control of the test runner is the file input form field located at the top of the page.† This control is meant to hold a path to a test page file or test page suite file.† Now letís look at a very simple example of a JsUnit test page.

<html>

<title>A unit test for byrne.SystemUnderTest class</title>

<head>

<script type='text/javascript' src='../jsunit/app/jsUnitCore.js'></script>

<script type='text/javascript' src='../app/system_under_test.js'></script>

<script type='text/javascript'>

 

††† function setUp(){

††††† // perform fixture set up

††† }

†††

††† function tearDown() {

††††† // clean up

††† }

 

††† function testOneThing(){

††††† // instantiating a SystemUnderTest, a class in the byrne namespace

††††† var sut = new byrne.SystemUnderTest();

††††† var thing = sut.oneThing();

††††† assertEquals(1, thing);

††† }

†††

††† function testAnotherThing(){

††††† var sut = new byrne.SystemUnderTest();

††††† var thing = sut.anotherThing();

††††† assertNotEquals(1, thing);

††† }

 

</script>

</head>

<body/>

</html>

 

JsUnit has many things in common with other xUnit frameworks.† As you might expect the test runner loads the test page, and calls each test function.† Each test function call is sandwiched between a call to setUp and tearDown.† The setUp function provides the test author with his or her opportunity to optionally construct a test fixture. The test fixture refers to state intended to be the fixed for all tests in the page. The tearDown function provides the test author with his or her opportunity to clean up or reset the test fixture.

There is however a subtle difference in the test life cycle in JsUnit compared to other xUnit frameworks.†Each test page is loaded into a separate window, preventing application code from overwriting the test framework code via open classes. Within each loaded window all unit test functions are invoked. The page is not reloaded for each test function.† In JUnit on the other hand, the test page would be represented by a test case, and the test runner would instantiate a separate test case instance for each test method. In other words,

JsUnit loads a test page with N test functions one time,
JUnit creates a test case with N test methods N times

JavaScript developers are therefore on a more slippery slope because changes to test page state can influence the outcomes of subsequent tests. A Java developer however is not exposed to this risk when making changes to a test case object. Why does JsUnit do this when the test page could simply be reloaded once per test? Because there are performance costs of recreating the DOM for every test function in a test suite. Fortunately JavaScript developers have to care less about side effects of mutable global state. On a programming platform such as the JVM or the CLR, a unit test which mutates a static variable can impact the outcome of all subsequent tests in the entire test suite, not just those tests belonging to the same test case.

The jsUnitCore.js script must be embedded in all test pages. This important file is located in the app directory of the unzipped JsUnit download file.† It contains a handful of assertion functions that behave more or less identical to other xUnit frameworks.† A subtle difference however stems from the fact that JavaScript has two notions of equality.† JavaScript has an equals operator and a threequals operator.† For example, the first expression below evaluates to true, the second evaluates to false:

  0 == false

  0 === false

How does this work? The equals operator is not as strict as the threequals operator, allowing the runtime to perform type conversion for the first boolean expression. Thus it is understandable for a novice to assume the following assertion will pass.

  assertEquals(false, 0);

This assertion will in fact fail the test because the assertion functions provided by the JsUnit framework use the stricter threequals operator as opposed to the equals operator for all comparisons. By avoiding the equals operator JsUnit can avoid many false positive test runs.

Stubs vs. Mocks

Letís look at stubs and mock objects with our running example, a slot machine.† Because this unit test is focused on a single object, we will create a constructor. A test instance of this constructor will be referred to it as the system under test.†Now letís write a simple test to render the slot machine.†

    function testRender() {

††††† var buttonStub = {};

††††† var balanceStub = {};

††††† var reelsStub = [{},{},{}];

††††† var randomNumbers = [2, 1, 3];

††††† var randomStub = function(){return randomNumbers.shift();};

 

††††† var slotMachine = new byrne.SlotMachine(buttonStub, balanceStub, reelsStub, randomStub);

††††† slotMachine.render();

 

††††† assertEquals("Pay to play", buttonStub.value);

††††† assertTrue(buttonStub.disabled);

††††† assertEquals(0, balanceStub.innerHTML);

††††† assertEquals('images/2.jpg', reelsStub[0].src);

††††† assertEquals('images/1.jpg', reelsStub[1].src);

††††† assertEquals('images/3.jpg', reelsStub[2].src);

    }

The testRender function stubs out two DOM elements, injects them into the constructor of the system under test and calls the render method.† The test ends with assertions for the expected side effects of the render method.† Notice that by stubbing out the DOM elements we can test the side effects of the render method without actually doing anything that may invalidate other tests within this test page.† Using real DOM elements on the other hand, and in some situations there are good reasons for this, would force us to reset the DOM state at the end of each test or in tearDown .

The system under test does not make use of a global function call to Math.random in order to determine the initial image state of the reels.† The slot machine instead relies on what is provided to it at creation time in order to obtain these numbers.† This allows us to test a non deterministic piece of software as though it were entirely predictable.† This test could have simply overwritten Math.random, leaving us with the same slippery slope of side effects.

The object's constructor and its render method look like this:

/**

†* Constructor for the slot machine.

†*/

byrne.SlotMachine = function(buttonElement, balanceElement, reels, random, networkClient) {

† this.buttonElement = buttonElement;

this.balanceElement = balanceElement;

this.reels = reels;

this.random = random;

this.networkClient = networkClient;

this.balance = 0;

};

 

byrne.SlotMachine.prototype.render = function() {

† this.buttonElement.disabled = true;

this.buttonElement.value = 'Pay to play';

this.balanceElement.innerHTML = 0;

for(var i = 0; i < this.reels.length;){

††† this.reels[i++].src = 'images/' + this.random() + '.jpg';
  }

};

Letís put some money into this the slot machine.† In this scenario, the slot machine will make an asynchronous call back to the server in order to retrieve the userís balance.† This is challenging because each instance makes an AJAX call to the server.† Doing this results in an exception because there is no network in a unit test. A unit test should be free of side effects, and IO certainly falls under this category.

function testGetBalanceGoesToNetwork(){

† var url, callback;

var networkStub = {

††† send : function() {

††††† url = arguments[0];

††††† callback = arguments[1];

††† }

† };

 

var slotMachine = new byrne.SlotMachine(null, null, null, null, networkStub);

 

† slotMachine.getBalance();

 

† assertEquals('/getBalance.jsp', url);

† assertEquals('function', typeof callback);

}

This test stubs out the network. What is a stub? and how is a stub different than a mock? Many developers often confuse these two terms as synonyms.† The testing community reserves the word stub for state based testing.† In JavaScript, this usually just means a simple object literal with hard coded return values.† The word mock on the other hand is reserved for interaction testing.† Mocks can be behaviorally trained.† These behaviors interact with the system under test and the interaction can be verified.

By stubbing out the network client we can now test the getBalance method.† The object literal applied to the constructor records itís interaction with the system under test via local variables url and callback, giving us something to perform assertions on at the end of the test.† Unfortunately we have used the wrong tool for the job.† This is a classic example of the limitations of stubs and why mock objects serve a purpose. ††The purpose of this test is not to verify the behavior of the system under test given a certain injected state; this test is concerned with verifying the interaction of a byrne.SlotMachine instance with one of its collaborators, the network client.† If you look closely, you can see that we have created our own miniature mocking framework.† Now letís change the test to use JsMock, a mock object library for JavaScript. We do this by adding a single script tag to the test page and rewriting the test like this:

    <script type='text/javascript' src='../jsmock/jsmock.js'></script>

††† function testGetBalanceWithMocks(){

††††† var mockControl = new MockControl();

††††† var networkMock = mockControl.createMock({

††††††† send : function() {}

††††† });

††††† networkMock.expects().send('/getBalance.jsp', TypeOf.isA(Function));

†††††

††††† var slotMachine = new byrne.SlotMachine(null, null, null, null, networkMock);

 

††††† slotMachine.getBalance();

 

††††† mockControl.verify();

††† }

The test now gives us the same feedback in fewer lines of code and we have laid the groundwork for much cleaner foundation to build on for future tests.† How does this work?† The first line of code creates an object using the MockControl constructor provided by JsMock. The test then creates a mock object with a send method.† In an application with an actual NetworkClient class, we would not even have to supply a object literal skeleton.† JsMock would be able to infer this via the prototype:

  var †mock = mockControl.createMock(NetworkClient.prototype);

Once a mock object for the network client has been created it is programmed to expect the send method to be called once, with certain parameters.† We care that the server resource name is correct and that the second argument is a callback function.† The mock object is injected into the constructor of the system under test and the test concludes by verifying this interaction via the verify method of the MockControl object.† If for any reason the slot machine implementation didn't call the send method on the network client, or if it were to fail to meet the parameter expectations, the verify method would throw an exception and the test would fail.

Now letís make another test to verify when and how often a byrne.SlotMachine instance goes to the network.† If the getBalance method is called before the server response is complete, we donít want the balance to be retrieved twice.† This could result in the slot machine balance reflecting the userís balance twice over, and some extra bandwidth.

††† function testGetBalanceWithMocksToTheNetworkOnce(){

††††† var mockControl = new MockControl();

††††† var networkMock = mockControl.createMock({

††††††† send : function() {}

††††† });

††††† networkMock.expects().send('/getBalance.jsp', TypeOf.isA(Function));

†††††

††††† var slotMachine = new byrne.SlotMachine(null, null, null, null, networkMock);

 

††††† slotMachine.getBalance();

††††† slotMachine.getBalance(); // no response from server yet

††††† slotMachine.getBalance(); // still no response

 

††††† mockControl.verify();

††† }

Remember our first crack at this?† When we created our own miniature mocking framework?† That might have seemed like a practical solution then but imagine how much code it would take to test an interaction like this.† Just for arguments sake, letís look at a flawed pure stub solution.

††† function testGetBalanceFlawed(){

††††† var networkStub = {

††††††† send : function() {

††††††††† if(this.called)

††††††††††† throw new Error('This should not be called > 1 time');

††††††††† this.called = true;

††††††† }

††††† };

 

††††† var slotMachine = new byrne.SlotMachine(null, null, null, null, networkStub);

 

††††† slotMachine.getBalance();

††††† slotMachine.getBalance(); // no response from server yet

††††† slotMachine.getBalance(); // still no response

††† }

This test asserts that the network client is only used once by simply throwing an error from the network stub after the first use of it.† There is a subtle problem here because the test is handing control of the assertion over to the object itís supposed to be testing.† For example, if system under test were to call the send function of the network stub multiple times, but swallow the exceptions thrown by it, the test would never fail because the test runner would simply never receive notification of a problem.†† There are ways around this of course but all solutions boil down to creating a miniature mocking framework or just going with a general purpose one like JsMock.

JsMock does not just give us the ability to test method call order and parameter values.† Hereís a test to demonstrate how the slot machine behaves in the event of a network failure.

††† function testGetBalanceWithFailure(){

††††† var buttonStub = {};

††††† var mockControl = new MockControl();

††††† var networkMock = mockControl.createMock({

††††††† send : function() {}

††††† });

††††† networkMock.expects()

†††††† †.send('/getBalance.jsp', TypeOf.isA(Function))

†††††† †.andThrow('network failure');

†††††

††††† var slotMachine = new byrne.SlotMachine(buttonStub, null, null, null, networkMock);

 

††††† slotMachine.getBalance();

 

††††† assertEquals("Sorry, can't talk to the server right now", buttonStub.value);

††††† mockControl.verify();

††† }

Here we are verifying that the slot machine can fail gracefully in the event of a network failure.† This is a good example of when unit testing can really outperform system integration testing.† Can you imagine how much money and time it would cost to manually simulate a network failure for every integration point with the server during every a QA/Release cycle?

The getBalance method implementation now looks like this:

byrne.SlotMachine.prototype.getBalance = function() {

† if(this.balanceRequested)

††† return;†

try{

††† // this line of code requires the very excellent functional.js

††† // library, found at http://osteele.com/sources/javascript/functional

††† this.networkClient.send('/getBalance.jsp', this.deposit.bind(this));

††† this.balanceRequested = true;

}catch(e){

††† this.buttonElement.value = "Sorry, can't talk to the server right now";

}

};

One drawback of mocks is that they are considerable more coupled to the system under test than a stub, at least out of the box.† You want a test to fail when the system under test fails to produce a specific behavior Ė you don't want a test to fail on every change to encapsulated implementation details.† To remedy this situation JsMock provides the ability to relax expectations.† Youíve seen an example of this already.† When we trained our network mock object, we wrote this:

  networkMock.expects().send('/getBalance.jsp', TypeOf.isA(Function));

We didnít specify which function callback would be applied as the second argument, just that a function callback would be applied.† If we wanted to loosen these expectations even further, we might try something like this:

  networkMock.expects().send(TypeOf.isA(String), TypeOf.isA(Function));

If we wanted a reference to the actual callback being passed to the send method of the network client mock, we could make use of the andStub method of the JsMock framework:

  var depositCallback;
  networkMock.expects()
    .send('/getBalance.jsp', TypeOf.isA(Function))
    .andStub( function(){depositCallback = arguments[1];} );
  depositCallback({responseText:"10"});

Two gotchas on mock objects before we proceed.† Notice how each test ends with a call to the verify method of the MockControl.† This is important.† A unit test that does not call verify is a unit test that cannot fail.† It occurs to many developers after authoring a few standard unit test functions that it would be better to move the call to verify from the test functions to the tearDown function.† While this will save you a few lines of code and it relieves you of ever having to remember this very important detail at the end of each test function, it unfortunately will present you with a new problem.† An exception thrown in tearDown can be masked by the first exception thrown by the test.† A second pitfall is that some developers who are new to mock objects will often overuse them.† More specifically, they try to use them as a all out replacement for stubs.† Donít do this.† Use stubs for state based testing, use mocks for interaction based testing.

A Winning Scenario Test

We can use everything weíve learned to test the following scenario.† This test simulates a user losing then winning with the slot machine.†

††† function testLoseThenWin(){

††††† var buttonStub = {};

††††† var balanceStub = {};

††††† var reelsStub = [{},{},{}];

††††† // a losing combination, followed by a winning combination

††††† var randomNumbers = [2, 1, 3].concat([4, 4, 4]);

††††† var randomStub = function(){return randomNumbers.shift();};

†††††

††††† var slotMachine = new byrne.SlotMachine(buttonStub, balanceStub, reelsStub, randomStub);

††††† var balance = 10;

††††† slotMachine.deposit({responseText: String(balance)});

†††††

††††† slotMachine.play();

†††††

††††† assertEquals(balance - 1, balanceStub.innerHTML);

††††† assertEquals('Sorry, try again', buttonStub.value);

†††††

††††† slotMachine.play();

 

††††† assertEquals('balance - 2 + 40', 48, balanceStub.innerHTML);

††††† assertEquals('You Won!', buttonStub.value);†††††

††††† assertEquals('images/4.jpg', reelsStub[0].src);

††††† assertEquals('images/4.jpg', reelsStub[1].src);

††††† assertEquals('images/4.jpg', reelsStub[2].src);†††††

††† }

The play method implementation of the byrne.SlotMachine class looks like this:

byrne.SlotMachine.prototype.play = function(){

† var outcomes = [];

var msg = "Sorry, try again";

for(var i = 0; i < this.reels.length; i++){

††† this.reels[i].src = 'images/' + (outcomes[i] = this.random()) + '.jpg';

}

† if(outcomes[0] == outcomes[1] && outcomes[0] == outcomes[2]){

††† msg = "You Won!";

††† this.balance += (outcomes[0] * 10);

}

† this.buttonElement.value = msg;

this.balanceElement.innerHTML = --this.balance;

};

winning.bmp

And finally a working demonstration of a slot machine:

<html>

<title>A Slot Machine Demonstration</title>

<head>

<script type='text/javascript' src='functional.js'></script>

<script type='text/javascript' src='slot_machine.js'></script>

† <script type='text/javascript' src='network_client.js'></script>

<script type='text/javascript'>

 

††† window.onload = function(){

††††† ††† var leftReel = document.getElementById('leftReel');

††††† † ††var middleReel = document.getElementById('middleReel');

††††† † ††var rightReel = document.getElementById('rightReel');

††††† † ††var random = function(){

††††† ††††† return Math.floor(Math.random()*5) + 1; // generate 1 through 5

††††† † †};

††††† ††† slotMachine = new byrne.SlotMachine(document.getElementById('buttonElement'),

††††† ††††††††††††††††††††††††††††††††††††††† document.getElementById('balanceElement'),

††††† ††††††††††††††††††††††††††††††††††††††† [leftReel, middleReel, rightReel],

††††† ††††††††††††††††††††††††††††††††† ††††††random,

††††† ††††††††††††††††††††††††††††††††††††††† new NetworkClient());

††††† ††† slotMachine.render();

††††† ††† slotMachine.getBalance();

†};

†††

</script>

</head>

<body id='body'>

 

<div style="text-align:center; background-color:#BFE4FF; padding: 5px; width: 160px;">

††† <div>Slot Machine Widget</div>

††† <div style="padding: 5 0 5 0;">

††††† <img id='leftReel'/>

††††† <img id='middleReel'/>

††††† <img id='rightReel'/>

††† </div>

††† <div>Balance: <span id="balanceElement"></span></div>

††† <input id="buttonElement" style="width:150px" type="button" onclick="slotMachine.play()"></input>

</div>

 

</body>

</html>

 

Reference Materials

         JSMock is a fully featured Mock Object library for JavaScript authored by Justin DeWind

         JsUnit is a Unit Testing framework for client-side (in-browser) JavaScript

         Mocks Arenít Stubs, an article by Martin Fowler

         Functional is a library for functional programming in JavaScript authored by Oliver Steele

         Dependency Injection, an article by Martin Fowler

About the Author

Dennis Byrne works for DRW Trading, a proprietary trading firm and market maker.† He's a writer, presenter and active member of the open source community.