Tuesday, April 13, 2010

Test Driven Development

I'm trying the technique of Test Driven Development (TDD).  It feels a bit weird, but so far it has worked marvellously.

I haven't done any design, other than have a general sense of what I want to do ("Write some objects to encapsulate the database...").  In TDD, the design is supposed to emerge from repeated short cycles of Test-Code-Repeat.

The idea is to start by writing a small test for some equally small piece of code.   The test inevitably fails, because there is no code yet.   Create or fix the code (should only require a few lines), and then write another test.  Eventually, in tiny bite-sized pieces, a fully-developed program emerges.

My short description here doesn't do justice.  If you are interested, take a peek at Wikipedia's article 




Done.  I've written the database objects using TDD.  When I look at the resulting code, I'm surprised by how well TDD worked for me.

The module that I finished with was much more elegant than the stuff than I usually write, and I have a high confidence that it works because about half of it tests the other half (in truth, both halves test each other).

Programming in TDD is also quite enjoyable, there's a feeling of steady progress.  But it sure feels weird.  I'm really a design-first guy at heart.

The weirdest part is that TDD drives you to start with trivial stuff that you might not even consider including in your project.   If I were designing, my mind would be on focused inserts and updates and primary keys.  But TDD doesn't let you start there.  You need to pick something simple and test-able for the first test.

I started on the 'test' side, and the simplest test I could think of was "Does a test table exists, roughly "assertTRUE(tableExists('Test'));'.  Ran it, and of course it failed,  the method 'tableExists()' was just an empty stub.

Then I went to the 'code' side, and filled in the stub with code to test for whether a specific table existed.  Not a hard job.  But not where I would have started normally.  This is a pretty unimportant function once the system is running, and likely I would never have gotten to adding it to my class.

OK, what's next?  Well, now I need to create that table.   Normally, I would write a SQL script that created the whole system and run it through the MySQL console.  But that's hard to write a test for.  So I extended the test to say "If it doesn't exist, then create it", and the second function I wrote created a single table.

Again, something I likely would have not bothered writing.  As soon as my 'create' function worked, my test started returning SUCCESS instead of FAIL.

But the logical next step on the 'test' side was a table-delete function so I could rerun it.  One day I'll need it, but these maintenance functions are not where I would have spent my first day programming.

I really want the code for adding a record - that's the meat-and-potatoes.  But my inner-tester writes 'Count the records, add one, count again, assert that the count has increased'.  So I end up writing a function that returns the count of records.  Again, something that might never have gotten written with older techniques.




It's all done, didn't take long for the full datatable class to emerge.  The whole step was only a few hour's work.  I'm building on Joomla!, so I'm using their JDatabase library for the low-level interface.  Finding the right methods is the most time-consuming part of this program.

My code follows 'Abstract Factory Pattern' - there is a 'factory' class for data tables, an 'abstract' class with interfaces, and then a handful of concrete classes that represent each of the tables and I want to implement.  

I had to spend much more time thinking about how to make functions test-able, something I have often ignored in the past.






Most importantly, I finished with really clean code.  TDD guarantees that your code is exhaustively tested, I have confidence that I won't have to come back and fix bugs.  

And when I inevitably do have to come back to refactor something, there is a sophisticated test program that will verify the tiny details and ensure that I don't break anything.



Next step?  Well, when you fire up the original Agenda, it first asks which file you want to open.  I now have a place to keep my list of files, so let's write a class to display what 'Files' are in Agenda, and select one to open.

0 comments: