Today we wrote some complicated FX business logic tests. Things like making sure that the FX library can calculate a EURUSDCAD 1M rate - it can; or a USDCAD ON rate - it can’t and nobody had spotted the fact that it was out because it’s way down in the 0.00001’s of the rate.
It has taken us around a week to get here. The initial code was a tightly coupled mess.
On Friday we got to the point where the FX buiness logic code was suitably decoupled from the display logic that we could write our first test for the business logic. In the words of Homer Simpson, “Woo hoo!”.
Test one was a simple one; can we construct the FX ’live point’ object. After adding a few interfaces and mocking up a service provider or two we could.
The object uses two-stage construction; not ideal, but the code doesn’t use exceptions yet.
I’ve spent the morning doing test driven development, properly; writing tests first and everything. It works, it’s faster and it’s addictive.
The current work item for the online game is to take the existing game play and adjust it to include a few of the more complex special cases. This alters the game play a little, it alters who goes first, etc. It took a while for me to elicit enough detail to fully understand the requirements, but now I have it.
A couple of days ago I posted some untestable code. I’ve had a couple of emails saying that people couldn’t see why the code was untestable. Here’s why, and here’s how to fix it.
The code shown manages a list of listeners who are interested in being notified when the system date changes. It’s externally driven by calling CheckForChange(), this could be called in response to a timer event or from a polling thread, or whatever.
Three bugs went into a program; a memory leak, a misunderstood interface and a deadlock…
The memory leak was easy to track down as it was in the code that was covered by the test harness. So we just instrument the code with Bounds Checker and run the test. A reference counted object was being mishandled and leaking. Easy fix.
The interface issue was harder. I was using a std::list<> to manage a list of items and every so often needed to walk the list and remove some entries.
It’s easy to write untestable code. It’s natural. Most code that we write will be hard to test unless we explicitly think about testing when we write it…
This code is really simple, yet it’s untestable. Why?
DateChangeManager.h class CDateChangeManager { public : class Listener { public : virtual void OnDateChange( const CTime &oldDate, const CTime &newDate) = 0; protected : ~Listener() {} }; CDateChangeManager(); typedef unsigned long Cookie; Cookie RegisterForNotification( Listener &listener); void UnRegisterForNotification( const Cookie &cookie); void CheckForChange(); CTime GetToday() const; private : CTime m_today; typedef std::map<Cookie, Listener *> Listeners; Listeners m_listeners; CCriticalSection m_criticalSection; // No copies - do not implement CDateChangeManager(const CDateChangeManager &rhs); CDateChangeManager &operator=(const CDateChangeManager &rhs); }; DateChangeManager.
So, we have currency based limits and they’re in millions.
Us: “Are they always going to be in millions for all ccys?”
Them: “Yup.”
Coded, tested, delivered.
Them: “can we have the JPY limits in billions?”
Us: “US or European?” ;)
So now limits have exponents and exponents have names. I dummied up the functions to just return an exponent of 6 and a name of “m”. That worked. Then I added tests for it… I took out the “* 100000” and called the new getLimitValue() function that takes the display limit and applies the exponent…
This morning I wasted some time tracking down bugs in the multi-threaded online game engine that I’m writing for a client. Now I have tests. Tests are good.
The bugs were little things, they always are. Most of the wasted time was spent simply getting the server into a state where I could poke it just right and make the bug wave a white flag. For example, a bug only shows up when you have 3 players and one does something particular in round 2 and another leaves when he shouldn’t in round 3.
Another week another release. Well, almost. The plan was to release today. The plan ignored the fact that most of the team are at a wedding this weekend and nobody was around today and nobody’s around on Monday…
The latest release included some new functionality and a lot of refactoring. Given the amount of code that had been ’touched’ I recommended a longer test phase than we usually planned, i.e. more than 1 day.
Kent Beck demonstrates the testing side of XP by separating it out into its own simple methodology. Test-Driven Development is exactly what it says it is. The entire design and development effort is driven by the tests that you write and you’re encouraged to write those tests first…
Parts one and two contain worked examples of Test-Driven Development. Some will probably say that they’re too simple, but I’ve found that even the most complex domain usually ends up as relatively simple code if you develop in this way.