Archive

Posts Tagged ‘code’

How I See Software Systems

May 14th, 2009
Comments Off

I’m pretty sure that I have a knack for designing software systems that make sense. This might be because I have a ridiculously inflated sense of self worth, but I choose to ignore this possibility.

Rather than try to establish any kind of track record or credentials that establish that I am honestly entitled to lecturing about anything, I am going to try to explain what properties I consider to be important in a well-built software system, and take a stab at explaining what benefits are to be had from them. (further disclaimer: I make no claim whatsoever that any of these ideas are the least bit novel)

So, without further delay…

Well-Constructed Software Systems are Object Oriented

Controversy!

I am not going to try to say that building a procedurally-styled system will directly cause a nuclear war with Russia. There are lots of smart folk out there who have figured out a large number of successful ways to build software.

I will, however, say that objects are a super-robust, structured way to parameterize behaviours, and that hiding implementations behind polymorphic interfaces is fantastic for simplifying TDD. I think these things are super-important and I derive oodles of joy from working with code that is built this way. Unless I totally screw this blog post up, the reasoning will become apparent in a few paragraphs.

Well-Constructed Software is Unit Testable

There are people out there who have built amazing software without any automated testing, and there are people who just go for full-on integration tests all the time, and there are even people who do lots of complicated algebra to prove that their application is correct in all cases. In my opinion, the best camp to be in is the one where you carpet your application with a thick nest of unit tests, and throw in integration tests wherever it feels good. (and whenever you fix a bug)

This is a great situation to be in because unit tests are very, very fast, and they are the only kind of automated test that you can really count on to be 100% reliable. Further, once you get good at them, you can punt your slow, unreliable (but oh-so-handy) functional tests on some build slaves somewhere else, so they do not intrude upon your continuous integration/deployment/stuff.

Well-Constructed Software Systems Mostly Form a Directed Acyclic Graph

The previous point was actually sort of a precursor to this one. Objects and interfaces were exciting 20 years ago maybe, but we are in the distant future (the year 2000!) now and that means that OO is passe.

A much more interesting and useful claim to make, I think, is that the references your object system has should mostly form a DAG. What does that mean? In short, it means that you have classes “at the top” of your application (you might have one called Application or MainWindow, to pick an example), and, from there, the references mostly trickle “down” to implementation details like file handles, sockets, and textures.

More to the point, it means that your “down” classes do not hold references that go “up”. If you show me a design that requires that your Texture class hold a reference to its Application, my instinct will be that something is not right.

Further, it means that you really want to avoid having “sibling” objects that form a circular relationship. Thar be dragons. And no, I’m completely incapable of articulating why. If cornered, about the best I can do is mumble some incoherent bullshit about how it offends my senses and how it will, at the least, throw more than a few garbage collection implementations for a loop. (which, I should point out, can become particularly serious if the cycle spans different programming runtimes, like Python and C++)

This is a great property for a software system to have because you can test your “lower” types without even needing to construct the “higher” ones at all. This means that individual unit tests need to execute less code, which means easier maintenance, higher orthogonality, and faster tests.

There’s a catch with this one, of course. When I say “mostly,” I really do mean mostly. All too often, you’re going to be in some situation where you really need to attach a listener or a lambda or whatever. Don’t sweat it, but you really want to take a closer look at those upward-pointing references. You might want to make them weak references instead of strong, and, if you’re coding in a statically-typed language, you almost certainly want that reference to have an interface type, rather than a concrete class. Sometimes, you can even clip those references entirely by leveraging method return values.

Well-Constructed Software Systems Do Not Depend on Global State

Remember the bit about the Directed Acyclic Graph? Global state is like having an object that is referred to by every other object and function in your entire application. It’s a ridiculously bad idea.

That being said, I periodically break this all the time because I’m lazy as hell and I’m always so sure that I’ll get away clean this time. If there’s anything I love to cut, it’s corners. Most of the time, I hurt dearly for my transgressions. I fail to learn my lesson because I’m retarded.

The thing is, I really like test-driven development, and if there’s anything that wrecks TDD, it’s unreliable tests. And if there’s anything that makes tests unreliable, it’s state that leaks in from outside the test harness. That’s what global variables are.

It sounds really painful, but what you really should be doing is injecting all your dependencies. In practice, it really is painful, but only until you realize that globe-spanning dependencies are generally a sign that your design has other problems.

What this means is that your objects always work with whatever you give them. If you’re in a test and you give an object fakes, you have pretty good assurance that it’s only going to talk to fakes.

One testing practice I have had great success with is only mocking out the hardware level stuff: Filesystem, network, sound, video, windowing, and so on. These are ultimately the things that make tests slow and frail, so they clearly have no place in my unit tests. Once the hardware stuff is faked out, the other objects in the system cease to pose a threat to testability: are just bits swimming around in memory. When all your dependencies are explicit, formal parameters, this is very easy to accomplish.

so…

There’s a reason for all of these painful rules, and it basically boils down to this:

def testMyCrap(self):
    hw = FakeHardwareServices()
    o = MyCrap(hw)
    self.assertEqual('something', o.doSomething())

Bam. That’s it, right there.

Looks like the toy example everyone uses to introduce TDD, right? Sure, but there’s a trick: I have gone out of my way to ensure that all of my objects accept their dependencies as formal parameters, and I have ensured that none of my objects gank or poke random global objects. I’ve also ensured that none of my objects require ridiculous heavy upstream dependencies to clutter everything up.

I know that I can test every single class in my application from this template, and I can do it in a way that will run instantly and pass 100% of the time.

Uncategorized

Random Information: C++ Function Pointer Syntax

February 20th, 2009
Comments Off

C++ pointer syntax is many things, but is not pretty. It is so un-pretty that, around the office, I have somehow wound up the local expert in the field of typing them out.

The syntax is really arbitrary and complicated, but, if you strip it down, it’s pretty basic.

Non-class-method pointers are pretty familiar. They come up all the time in C, and they look like this:

ReturnType (CALLING_CONVENTION *NAME)(Arg, Arg, ...)

CALLING_CONVENTION is going to be something like __stdcall, __cdecl, __fastcall, or whatever. It is optional.
The “NAME” bit is the confusing part. That’s where the name of the type or the variable goes. For instance:

// declare a variable named 'ptr', that points to a
// function that takes an int, and returns an int
int (*ptr)(int);

// Make a typedef for a pointer to a function that takes no arguments,
// and returns a double
typedef double (*DoubleFunction)();

Pointer-to-method

These are almost the same, but you have a class name and a ::

// A variable named 'ptr' that is a method of type T,
// takes no arguments, and returns void
void (T::*ptr)();

// Declare a typedef for a method on the IDirect3D8 interface.
// (all COM methods use the __stdcall calling convention)
typedef HRESULT (__stdcall IDirect3D8::*D3D8Function)();

That’s about all the useful information there is to have about typing out function pointers that do various things. Just for kicks, let’s dive into insanity:

Function References

There is also a such a thing as a function reference. They’re just like other kinds of C++ references. ex:

void call_a_function_reference(void (__cdecl &this_is_the_parameter_name)(int)) {
    this_is_the_parameter_name(0);
}

const

oh god what the hell is going on here

void try_to_clobber_a_const_function_pointer(void (* const parameter_name)()) {
    parameter_name = 0; // illegal because this parameter is const
}

Uncategorized

Testable Web UI VII: Postback

February 19th, 2009
Comments Off

Last time, we got a bit derailed by some issues that were getting in the way of effective tests. Now that that’s all wrapped up, let’s implement selecting equipment.

The primary requirement here is that we have to display the player’s equipment, and when they select an item by clicking on it, we post to the server to save the player’s selection.

First, I populate the inventory list with the result of another JSON service. This is nothing new, really, so I will skip the details. However, I will need the __renderInventory function for the next step:

__renderInventory : function(inventory) {
    var n = this.__getChild('inventory');
    while (n.firstChild) {
        n.removeChild(n.firstChild);
    }

    for (var index in inventory) {
        var i = inventory[index];
        var li = document.createElement('li');
        li.setAttribute('class', 'inventory-item item-' + index);
        li.innerHTML = i.name;
        li.onclick = function(self, index) {
            self.__equipItem(index);
        }.bind(self, index);
        n.appendChild(li);
    }
},

__equipItem : function(index) {
    // TODO!
},

To write this test, I expanded FakeNetwork a bit, to encompass the ability to make POST requests as well as GET:

function FakeNetwork() {
    this.requests = {};
}

FakeNetwork.prototype = {
    asyncRequestJson : function(url, onComplete) {
        this.requests[url] = [null, onComplete];
    },

    post : function(url, data, onComplete) {
        this.requests[url] = [data, onComplete];
    },

    isRequestPending : function(url) {
        return url in this.requests;
    },

    getRequestData : function(url) {
        Assert.isTrue(this.isRequestPending(url), 'FakeNetwork: "' + url + '" has no data, as it is not pending');
        return this.requests[url][0];
    },

    completeRequest : function(url, result) {
        Assert.isTrue(this.isRequestPending(url), 'FakeNetwork: "' + url + '" is not pending.');
        this.requests[url][1](result);
        delete this.requests[url];
    },
};

This allows me to write the test:

testClickInventoryPostsToEquipService : function() {
    var sw = new StatusWidget('avatar-1', this.net);
    YAHOO.util.UserAction.click(getNode('avatar-1', 'equipment'));

    this.net.completeRequest('getInventory.json', [
        {name:'Clown Shoes', kickboxing: 9001}
    ]);

    Assert.isFalse(this.net.isRequestPending('updateEquipment.php'));
    YAHOO.util.UserAction.click(getNode('avatar-1', 'inventory-item item-0'));

    Assert.isTrue(this.net.isRequestPending('updateEquipment.php'), 'Service not dispatched');
    Assert.areEqual('Clown Shoes', this.net.getRequestData('updateEquipment.php').name);
},

The implementation is so simple as to be boring:

__equipItem : function(index) {
    this.net.post('updateEquipment.php', {name:this.inventory[index].name});
},

The last gap that is missing is that our real Network object can’t POST yet, but that is not sufficiently exciting to post here. (also, I don’t know how to write a functional test without writing some kind of CGI service, so I am going to cop out :D)

At this point, we’ve covered just about all of the major sticking points: introspecting CSS and the DOM at runtime, test isolation, asynchronous logic, mocking the network, and, of course, tests that are both reliable and really bloody fast: I ended with a pretty modest test suite of 7 tests that take (on my machine) 381ms to run. I’m about to call the experiment a raging success.

Tomorrow, I will talk about… something. It will be a surprise to everyone!

Source code

Uncategorized ,

Testable Web UI VI: The Evils of Shared State

February 18th, 2009
Comments Off

So, last time, we got to showing an inventory panel when clicking a menu item. Since I cheated last time and wrote the code to hide without a test, let’s start off by fixing our mistake:

testClickEquipmentTwiceHidesInventory : function() {
    YAHOO.util.UserAction.click(getNode("avatar-1", "equipment"));
    YAHOO.util.UserAction.click(getNode("avatar-1", "equipment"));

    Assert.areEqual(
        'none',
        getNode("avatar-1", "inventory").style["display"]
    );
}

Easy, right?

Wrong.

As written, this test fails because the initial state of the display is not what we expect it to be. In fact, we can’t rely on its state being anything because YUI Test explicitly states that it will run tests in whatever order it darn well pleases, and we’ve yet to do anything at all to try to clean up after ourselves.

We’ve come a fairly long way without bumping into this, but this always happens whenever your tests have some kind of external dependancy. Sooner or later, they start tromping all over each other, and you have to figure out what to do.

We have a few options:

  1. Make StatusWidget itself responsible for generating the HTML. This wouldn’t be bad from a design standpoint, but it means that the user might see an incomplete page if page loading takes too long.
  2. Write a setUp() function to initialize the state of our HTML to something that we expect. This isn’t quite as robust as destroying and recreating it every test run, but it will be fast and easy. The only catch is that we will have to keep changing our setup every time we add more to the markup.
  3. Wire the test setUp() to completely regenerate the HTML before every test is run. This is pretty good, as it doesn’t force us to make any design treadoffs that might not pan out, and it will be robust in the face of changes to our widget.

Generally speaking, you can usually assume that the scorched earth approach to setting up your testing data will be the right way. I’ll demonstrate why in a moment.

This, as it happens, is pretty easy. I’ll just put the HTML in a hidden ‘template’ div, and copy it before every test run:

setUp : function() {
    var t = document.getElementById('avatar-template');
    document.getElementById('avatar-1').innerHTML = t.innerHTML;
},

We also need to make a tiny change to the markup:

<div id="avatar-1" class="avatar"></div>

<div style="display:none">
    <div id="avatar-template" class="avatar">
        <div class="name">andy</div>
        <img class="avpic" src="avatar.png" />
        <div class="details">
            <div class="hitpoints_panel">HP:
                <span class="hit_point_score">5</span>
            </div>
            <div class="kickboxing_panel">Kickboxing:
                <span class="kickboxing_score">6</span>
            </div>
            <div class="linear_algebra_panel">Linear Algebra:
                <span class="linear_algebra_score">7</span>
            </div>
            <div class="cross_stitching_panel">Cross-stitching:
                <span class="cross_stitching_score">8</span>
            </div>
        </div>
        <div class="main_menu">
            <div class="equipment">Equipment</div>
        </div>
        <ul class="inventory"><li>Filler</li></ul>
    </div>
</div>

This still fails, though, and when I click the Equipment button manually, it fails to do anything. What gives?

This does:

testClickEquipmentExpandsInventory : function() {
    // Conspicuous lack of: var sw = new StatusWidget('avatar-1');
    YAHOO.util.UserAction.click(getNode("avatar-1", "equipment"));

    Assert.areEqual(
        'block',
        getNode("avatar-1", "inventory").style["display"]
    );
},

As you can see, test dependancies are insidious, and, if we hadn’t pulled out the big hammer and blown the DOM away before every test, we would never have noticed this particular bug.

Tomorrow, we’ll get to populating the inventory.

Source code

Uncategorized ,

Testable Web UI V: Clicky!

February 17th, 2009
Comments Off

Last time, I started diving into using a JSON service to pull statistics into an HTML widget. I stopped right after that point because I thought, at the time, that it was worth spending a moment to think about what the target design should be.

After spending a few minutes staring at the code, though, I think I’m okay: StatusWidget is fully complaint with the SRP, and whatever other rules I can think of. So, instead of pontificating on what separates a serviceable design from a superior design, I am instead going to code more things!

Let’s go back to the initial set of requirements:

  • I want to see my character’s picture, name, and statistics
  • I want to be able to pick some equipment for my character
  • I want to see how my character’s equipment affects its statistics

The first one seems to work fine, so let’s move on to selecting equipment. Time to start coding some interactivity!

What I’ll be doing next is a sort of ‘drawer’ interface: A “Show Inventory” button that, when clicked, will toggle between displaying and hiding an inventory view (that we also have to code)

The current pattern I have been running with is to just dump markup into the test file, which is probably not sustainable (TODO: figure this out), but for now, it seems to be scaling pretty well. Let’s throw a menu in:

<ul class="main_menu">
    <li class="equipment">Equipment</li>
</ul>
<ul class="inventory"><li>Filler</li></ul>

It doesn’t look like much, but we’ve just introduced another dependancy: we want the inventory to be hidden most of the time, so we need some CSS to do this:

.avatar > .inventory {
    display: none;
}

There. Now we can’t see the filler.

That was fun. Now let’s make the button toggle the visibility.

testClickEquipmentExpandsInventory : function() {
    YAHOO.util.UserAction.click(getNode("avatar-1", "equipment"));

    Assert.areEqual(
        'block',
        getNode("avatar-1", "inventory").style["display"]
    );
}

I spent almost an hour trying to figure out how to test whether the element was visible, and I have a feeling that even this is not quite airtight. (I will ask one of my HTML god friends about this later) Nevertheless, I get a red bar, so I am going to forge ahead!

Now to make my hard-earned test pass:

function StatusWidget() {
    /* stuff we've already seen before */

    var self = this;
    this.__getChild("equipment").onclick = function() {
        self.toggleInventoryDisplay();
    };
}

/* ... */
StatusWidget.prototype = {
    toggleInventoryDisplay : function() {
        var n = this.__getChild("inventory");
        if (n.style['display'] == 'block') {
            n.style['display'] = 'none';
        } else {
            n.style['display'] = 'block';
        }
    },

    /* stuff */
}

This test will consistently pass, but it suffers from a subtle, but very common problem that will wreck testing later. Identifying it is left as an exercise to the reader. (HINT: it has nothing to do with my terrible non-browser-compatible JS, or my pisspoor CSS. It is specifically a testability bug)

Tomorrow, I will fix it and (time permitting) populate the inventory list.

Source code

Uncategorized ,

Testable Web UI IV: YUI Test (plus a bit of mocking)

February 16th, 2009
Comments Off

So, last time, I was trying to write a test around requesting some JSON data from a service, and return it. Unfortunately, JsUnit is not up to the task of handling asynchronous logic at all, so I decided to ditch it in search of greener pastures.

Having spent some time working with YUI in the past, I settled on trying YUI Test out.

It took some finagling, but I successfully managed to convert my first test over to the new testing framework. It’s got quite a lot of boilerplate, so I won’t inline it, but you can see it here.

Now that we’ve regained lost ground, time to try what we were doing before. Here’s my test:

testCanRequestJson : function() {
    var self = this;
    asyncRequestJson('getStats.json', function(result) {
        self.resume(function() {
            Assert.areEqual('andy', result.name);
            Assert.areEqual(9999, result.hit_points);
            Assert.areEqual(1, result.kickboxing);
            Assert.areEqual(99, result.linear_algebra);
            Assert.areEqual(99, result.cross_stitching);
        });
    });

    self.wait();
},

The way it works is that the “self.wait();” call tells the testing framework to wait until a corresponding “self.resume(fn).” If the resume doesn’t come within a few seconds, the test fails. It’s a bit ugly, but it does the job admirably, and I don’t know how you’d do it better without fibres or coroutines or something, so I’m not about to complain too loudly.

This is great, as it demonstrates I can interact with the network in a sensible way, but it’s kind of annoying that I have to have an extra data file just to make the test run. I think it’s fine for a functional test like this, but I don’t want to have to write functional tests for everything.

The next step is to plug these values into my widget. Here’s my test:

testWidgetIsPopulated : function() {
    var grueStats = {
        name:"Grue",
        hit_points:1,
        kickboxing:1,
        linear_algebra:0,
        cross_stitching:99999
    };

    var net = new FakeNetwork();
    var sw = new StatusWidget('avatar-1',
        function (url, onComplete) { return net.asyncRequestJson(url, onComplete); }
    );

    net.completeRequest('getStats.json', grueStats);

    Assert.areEqual("Grue", sw.getName());
    Assert.areEqual(1, sw.getHitPoints());
    Assert.areEqual(1, sw.getKickboxingScore());
}

FakeNetwork? Whassat? It’s this:

function FakeNetwork() {
    this.requests = {};
}

FakeNetwork.prototype = {
    asyncRequestJson : function(url, onComplete) {
        this.requests[url] = onComplete;
    },

    completeRequest : function(url, result) {
        this.requests[url](result);
        delete this.requests[url];
    }
};

Simple, yes? This way, I don’t have to actually have a JSON service for every little thing, and I don’t have to worry about asynchronous results or network funkiness. Win!

Tomorrow, I will do some badly-needed refactoring, and lay out what the future direction of the design should be.

Source code

Uncategorized ,

Testable Web UI III: XMLHttpRequest, and the Limits of JsUnit

February 15th, 2009
Comments Off

Last time, I started building an HTML widget to display a character’s status for some hypothetical RPG. This, in short, involved getting JsUnit going, sketching out the general jist of what it ought to look like, and an initial sanity test to ensure that some baseline of functionality was rigged correctly: we have a widget that can, if given a div ID on a page, return what the currently displayed name and statistics are.

A grand start, but nothing that resembles useful code just yet. Next, let’s see if we can make this service query a web service to populate the statistics.

The first thing I did was to do some quick Googling to learn how to do this. Thus armed, I cheated and wrote an implementation first:

function getStats(url, onComplete) {
    var req = new XMLHttpRequest();
    req.onreadystatechange = function() {
        if (req.readyState == 4) {
            if (req.status == 200) {
                var result = jsonParse(req.responseText);
                onComplete(result);
            } else {
                throw new Error("Request failed: " + req.status);
            }
        }
    }

    req.open('GET', url, true);
    req.send(null);
}

jsonParse comes from some random bit of code that I found on the internet that parses JSON.

And the test:

function testRequestStats() {
    getStats('getStats.json', function(result) {
        assertEquals('andy', result.name);
        assertEquals(9999, result.hit_points);
        assertEquals(1, result.kickboxing);
        assertEquals(99, result.linear_algebra);
        assertEquals(99, result.cross_stitching);
    });
}

Now, to actually make this test work, I need to write a JSON “service:”

{"name":"andy","hit_points":9999, "kickboxing":1,"linear_algebra":99,"cross_stitching":99}

Yay, green bar, right? Not quite: I cheated and wrote the implementation first, which means that I have never seen this test go red. Sure enough, breaking the test still nets me a green bar.

So, what gives? After doing a bit of debugging using Firebug, I’ve come to the conclusion that the test is not waiting for my asynchronous callback to finish! What’s worse, JsUnit doesn’t provide any solution to this problem at all, and the ad-hoc solutions suggested by other developers are all terrible.

Well, screw this. I’m only a few days in, so I’m going to find another testing library.

Tomorrow, I’ll try YUI Test out.

Uncategorized

Testable Web UI II: JSUnit

February 14th, 2009
Comments Off

Last time, I talked about the hypothetical allure of small, fast, reliable, parallelizable unit tests, and embarking on something of a research project to get a feel for what it’s like to test-drive an HTML interface.

The first thing to do is pick a project, I have bad product sense, no imagination, and an unhealthy fixation on games, so I’m going to go with a character details widget for some hypothetical RPG. That means that our requirements lie somewhere along these lines:

  • I want to see my character’s picture, name, and statistics
  • I want to be able to pick some equipment for my character
  • I want to see how my character’s equipment affects its statistics

Second, here are my rules:

  • I am not going to go into details with respect to the backend at all. That is a subject that warrants a completely different discussion on its own.
  • I am only going to worry about FireFox 3. Making web applications that run on all major browsers is important, but I am primarily interested in learning how to test drive a web application that works, not learning how to write a compatible web application.
  • Full disclosure: I do not have much background in HTML/CSS/JS, so if you see me do something that looks stupid, it’s probably because I’m doing something stupid.

The first thing I always look for when coding in an unfamiliar language is an xUnit. Fortunately, there is a such a thing as JsUnit, so I didn’t have to go long without having something to install and set up. There was some weirdness surrounding Firefox’s security restrictions around JavaScript running from the local machine: I hacked around it just by setting up an Apache server on my workstation.

Since we are talking about a user interface here, I’m going to start with some markup:

<div id="avatar-1" class="avatar">
    <div class="name">andy</div>
    <img class="avpic" src="avatar.png" />
    <div class="details">
        <div class="hitpoints_panel">HP:
            <span class="hit_point_score">5</span>
        </div>
        <div class="kickboxing_panel">Kickboxing:
            <span class="kickboxing_score">6</span>
        </div>
        <div class="linear_algebra_panel">Linear Algebra:
            <span class="linear_algebra_score">7</span>
        </div>
        <div class="cross_stitching_panel">Cross-stitching:
            <span class="cross_stitching_score">8</span>
        </div>
    </div>
</div>

One thing I do know is that node IDs have to be unique across the whole page, so I’m going to take a swing at avoiding them for things that do not really, really need to be uniquely identified. We’ll see how that pans out.

I haven’t even written any JS yet, but I’m staring at that blob of crap that I just dumped into my own lap, and I’m already worried that anything I code will be riddled with bugs caused by me fudging up the DOM. The first tests I want to write will simply be testing that I can affect the display properly:

<html>
<head>
    <script language="JavaScript" type="text/javascript" src="jsunit/jsUnitCore.js"></script>
    <script language="JavaScript" type="text/javascript" src="StatusWidget.js"></script>
    <script type="text/javascript">
        function testCanFetchHitPoints() {
            var sw = new StatusWidget("avatar-1");
            assertEquals(5, sw.getHitPoints());
        }
    </script>
</head>
<body>
    ... that thing I pasted up above ...
</body>
</html>

Now that I have a blob of HTML as a piece of data to hang my StatusWidget on, and a red test, all I have to do is make it pass:

StatusWidget = function(id) {
    this.id = id;
};

StatusWidget.prototype = {
    getHitPoints : function() {
        var xpath = "//div[@id='" + this.id + "']//*[@class='hit_point_score']";
        var i = document.evaluate(
            xpath, document, null, XPathResult.ANY_TYPE, null
        );
        var e = i.iterateNext();
        return parseInt(e.innerHTML);
    }
};

Green bar means victory! Now I follow through and write tests to ensure that the rest all work. Normally, this might be considered redundant and pointless, but I don’t expect my markup to stay as-is forever, and I just know I’ll want to know when (not if) I go and screw the basics up.

function testCanFetchStats() {
    var sw = new StatusWidget("avatar-1");

    assertEquals("andy", sw.getName());
    assertEquals(5, sw.getHitPoints());
    assertEquals(6, sw.getKickboxingScore());
    assertEquals(7, sw.getLinearAlgebraScore());
    assertEquals(8, sw.getCrossStitchingScore());
}

And the adjusted implementation:

StatusWidget.prototype = {
    __getChild : function(className) {
        var xpath = "//div[@id='" + this.id + "']//*[@class='" + className + "']";
        var i = document.evaluate(
            xpath, document, null, XPathResult.ANY_TYPE, null
        );
        return i.iterateNext();
    },

    __getInt : function(className) {
        return parseInt(this.__getChild(className).innerHTML);
    },

    getName : function() {
        return this.__getChild("name").innerHTML;
    },

    getHitPoints : function() {
        return this.__getInt("hit_point_score");
    },

    getKickboxingScore : function() {
        return this.__getInt("kickboxing_score");
    },

    // etc
}

It works, so I’m pretty confident that I’ve reached some sort of baseline level of functionality. Next time, I will try pulling the statistics themselves from a web service. (and mocking!)

Source code

Uncategorized

Testable Web UI: Part I

February 13th, 2009
Comments Off

One of the things that I spend an awful lot of time thinking about at IMVU is testability. I am always interested in finding ways to write tests that are faster, simpler, and more reliable. Once your tests reach a certain size, the return on investment you get by improving the performance of your tests increases linearly, until it’s just too valuable to pass up. You can push the problem off somewhat by investing in hardware, and you can save quite a lot of engineer-muscle by doing this, but there’s eventually going to be a point where you just can’t resist looking for some way to shave just a few percentage points off your top-line test time.

At IMVU, our tests fall into a few basic categories. Some tests are pure PHP logic. Some are PHP logic that also interacts with MySQL, Solr, or a number of other external services. Others still use Selenium to drive an actual instance of Internet Explorer. (we also have a whole slew of tests for our 3D client, but they run on a separate buildslave cluster)

One idea that I find particularly intriguing is sacrificing acceptance tests in favour of unit tests. Unit tests are small, reliable, fast, and, while they don’t have the same kind of guaranteed real-world applicability as a full-system acceptance test, and you probably don’t want to trust them to catch unexpected regressions, it turns out that you can get quite a lot of mileage out of a sufficiently large swarm of them. Real unit tests can also be run one per thread on a dualcore machine for twice the effective performance. (and really, who even has a single-core CPU these days?)

The set of constraints placed upon frontend web tests is formidable:

  • The tests have to be fast
  • The tests have to be race-free on a platform that is built entirely on stateless, asynchronous I/O
  • The tests have to run on whatever browsers your customers use, because every browser has subtle quirks that will randomly break your code
  • Most web developers aren’t used to the idea of letting tests dictate the structure of their markup or scripts

Some of these issues are engineering culture problems. Some of these requirements are directly in conflict with each other, while others are, think, poorly understood and categorically dismissed as intractable problems that must be endured.

Just to pick one example, Internet Explorer 7 may be the world’s most popular browser, but it also has one of the slowest current-generation JavaScript engines. In particular, there are certain Selenium operations that run hundreds of times slower in IE than they do in Firefox. On the other hand, IE7 does certain things very differently from Firefox, and failing to take those differences into account can have catastrophic results.

So it seems that the choice is to sacrifice realism by testing on a faster browser, or to sacrifice speed to actually catch issues that will impact customers.

So, with all this in in mind, I believe that it is perfectly reasonable to structure your web frontend as two major classes of tests:

  • Frontend unit tests that do not touch the network. These tests can run on a fast JavaScript engine (like Firefox, or maybe even Chrome).
  • Full-blown acceptance tests that exercise everything (including the backend). Since these guys are the last line of defense, I’m pretty sure we really do have no choice but to run them on IE7.

I have found no shortage of literature and experience surrounding unit testing in general, and similarly, nobody is terribly confused about how to write effective acceptance tests. Within the specific field of test driving HTML interfaces, however, I have found precious little. So, in my next few posts, I am going to do a little bit of research, and share it all with the you, my adoring reader(s), as I go.

Next, I will talk about what I am going to try to build and my testing setup. (squeeeee project time!)

* I am endlessly entertained by my vivacious vocabulary. Also, alliteration is awesome!

Uncategorized

Random Thoughts about Adopting TDD

February 12th, 2009
Comments Off

There are plenty of folk who have realized that test-driven development is an amazing way to deliver better code faster. One of the claims I hear from a lot of places is that you should strive to unit test everything. Unit tests are fast and small and super reliable, and we all want that, right?

Sure do, but if you are trying to testify an existing project, it’s going to be really bloody hard to get to the point where you can write real unit tests at all, and they are by no means the only kinds of tests you’ll want anyway.

Adopting TDD in an existing project, with an existing team is generally very difficult not only because you have to acclimate your company culture, but because whatever code you’ve got is not written test-first.

Instead of going whole hog on pure unit tests, you can get a huge part of the benefit at a fraction of the cost simply by sucking up the speed hit (and reliability) by writing functional tests instead. In a desktop game, that may mean constructing the entire app and prodding it with windows messages. On the server, you might just want to try setting up engineer workstations as tiny production-clones and allowing your tests to connect to an actual database.

Whatever the application, figuring out this bit of technology gives you what you need to instate what I consider to be the first important engineering policy that will turn your company culture the right way: wherever possible, bugs should not be considered to be fixed until they have regression tests. Acceptance tests are easy to intuitively grasp, so it should be relatively easy to eliminate repeat bugs this way.

Further, this approach saves you some momentum in the short run: figuring out how to write code that can be unit tested is hard and will take some time. Bugfixes are perfect targets for this effort simply because the feature already broke. Instead of (well, in addition to, I suppose) committing to abstract pontification about what kind of tests you need for some new feature that customers have never seen, you have a certifiable, concrete weakness in your software. All you have to do is write a tiny program that asserts that it no longer exists.

If you can nail this, you have gotten your foot in the proverbial door. You will probably have gnarly, unreliable tests that are a pain to deal with for awhile, but you have them, which puts you in a position of learning how to make them better.

Uncategorized