software solutions / project leadership / agile coaching and training

JSView: Readable, object-oriented JavaScript TDD

Posted on October 25, 2010 in JavaScript, TDD

If you’ve tried to write JavaScript using TDD and your JavaScript deals with DOM elements, I’m guessing you’ve felt some pain. The toughest thing about writing tests against JavaScript is separating the HTML elements and the page from the JavaScript code. In the past when I’ve done this, I’ve created JavaScript classes and either passed UI elements into the constructor or by calling a method. So in the real page, I’ll have code that looks like this:

$(document).ready(function()
{
    var myClass = new MyClass(document.getElementById('blah'));
});

Then in my test class, I do something like this:

module("MyClass tests");
 
test("Test something", function()
{
    var element = new Object();
    var myClass = new MyClass(element);
});

This works fine… since JavaScript is a dynamic language, I can put any property on the “element” variable and then write tests against it, checking various properties that I know will get set on the real element (like “innerHTML”, for example).

Here’s the problem with this: if you’re writing tests first, you’re not sure what kind of elements you are going to have in the UI. Maybe “innerHTML” will get set on my element, or maybe “value”, or maybe something else. I just don’t know because I haven’t written the UI yet.

What I need it something that wraps the HTML element so that I can just call a function like getValue() and get the value, which may come from “value” in some cases, “innerHTML” in other cases, etc., depending on what kind of UI element I have.

jQuery to the rescue!

It turns out that we already have something that will wrap UI elements – jQuery. Using jQuery, we can search for elements by type (e.g. DIV, A, TR, etc.), class, or id. This works something like this:

var elements = $(’.MyCssClass’);

This returns all elements that have the MyCssClass class applied to them. But it doesn’t return the actual elements, it returns a wrapper around the HTML element. It also turns out that said wrapper has functions like val(), html(), and text() that I can use to get the values! Just what I needed.

But I still have a problem. If I’m trying to test code that deals with 20 different UI elements, that means that I am going to have to inject 20 UI elements or test doubles into my JavaScript code. This is both ugly and painful.

A simpler abstraction

JSView is an abstraction layer that sits between your JavaScript code and jQuery/HTML so that you can write tests for your JavaScript code without having the actual page and the DOM available in a test. Instead of calling jQuery methods directly, you will call methods that will interact with jQuery in your app, but will interact with a fake replacement for jQuery in your tests. Not only that, JSView will generate methods for you so that you can easily access values and events in a readable fashion. This will allow you to truly TDD your object-oriented JavaScript before the actual page even exists!

Now I can write object-oriented JavaScript classes that look something like this:

function Calculator(element, view)
{
    if (view == null)
        view = new jQueryView('Calculator', element);
 
    registerObjectProperties(this, view, ['FirstValue', 'SecondValue', 'Result', 'AddButton']);
 
    this.whenAddButtonIsClicked(function()
    {
        this.addValues();
    });
 
    this.addValues = function()
    {
        this.setResult(Number(this.getFirstValue()) + Number(this.getSecondValue()));
    }
}

Now I can write JavaScript unit tests that look like this (and without the DOM in place!):

module('When the Add button is clicked',
{
    setup: function()
    {
        calculator = new Calculator(null, new ViewTestDouble());
        calculator.setFirstValue(2);
        calculator.setSecondValue(3.5);
        calculator.clickAddButton();
    }
});
 
test('Then it should add the values in the first two textboxes and put the result in the third textbox', function()
{
    equals(calculator.getResult(), 5.5);
});

More details can be found here. The source code is up on github. I’ve been using and refining this for two years and I’m really liking how it’s turned out. I’m very interested in any feedback that you all might have, I’ve done a lot of JavaScript over the years but I’m not quite the JavaScript ninja that some other people are.

So get busy and write some tests!

No Comments »

No comments yet.

Leave a comment





SERVICES
SOFTWARE SOLUTIONS
I have over 10 years of software development experience on several different platforms (mostly Ruby and .NET). I recognize that software is expensive, so I'm always trying to find ways to speed up the software development process, but at the same time remembering that high quality is essential to building software that stands the test of time.
PROJECT LEADERSHIP
I have experience leading and architecting large Agile software projects and coordinating all aspects of a project's lifecycle. Whether you're looking for technical expertise or someone to lead all aspects of an Agile project, I have proven experience from multiple projects in different environments that can help make your project a success.
AGILE COACHING
I believe that Agile processes and tools should be applied with common sense. I've spent the last 6 years working on Agile projects as a consulant in many different environments, both in leadership roles and as a practitioner doing the work. I can help you find out how Agile can work best in your organization, not just apply a prescriptive process.
TEST DRIVEN DEVELOPMENT TRAINING
TDD Boot Camp is a hands-on, three day, comprehensive training course that will teach you all of the skills, tools, frameworks that you will need to use test-driven development to develop real world .NET applications. If you're not looking for something that intensive, check out the the half-day version.
Have any questions? Contact me for more information.
PRESENTATIONS
(presented with Paul Bahler and Kevin Chivington from IGS Energy)
From CodeMash 2011
An idea of how to make JavaScript testable, presented at Stir Trek 2011. The world of JavaScript frameworks has changed greatly since then, but I still agree with the concepts.
A description of how test-driven development works along with some hands-on examples.
From CodeMash 2010
From CodeMash 2010