Friday, March 25, 2011

3 Reasons Why You Need Test Driven Development

Having been through the initial phase of a startup, I've been responsible for fairly large projects on impossible timelines. With ever-changing specs and the pressure to ship, we never really got the time to write any automated tests.

It's not something that I'm proud of.

I took the first opportunity to experiment with "Test Driven Development" (TDD) when the support contract with a vendor ran out and I had to fix a bug myself. Based on this experience here's why I think every project needs test driven development:

#1 TDD will be your saviour when you're editing someone else's code


The project that the vendor had built for me wasn't very big, nor very complex. But I was in for a shock when I got my hands dirty to fix a bug. There weren't problems in the code, the code itself was the problem! It was, like, 10 WTFs per minute.

While I had to fix the bug fast, the last thing I wanted was to end up with messy and broken code. So, before fixing anything I wrote tests against the classes & methods that I planned on changing.

Thanks to these tests I was confident that my first patch to production didn't break any existing behaviour of the app.

Moral: Test suites will be your saviour when you're treading the "unknown code" territory without knowing what you may accidently break.

#2 TDD will be your guiding light when you're refactoring or rewriting code


The database structure was a mess. The ActiveRecord associations were a mess. The entire code base was a sad, sad mess. (DRY & basic structured programming, it seems, were concepts alien to the developer.)

I caught the developer's itch. I just had to fix this. I went on a refactoring spree.

The application was a 'rule engine' of sorts. It had 'marketing campaigns' comprising set of rules/filters to be applied on e-commerce transaction records. All transactions matching the rules/filters were to be assigned coupon codes.

The classes with the filtering logic were the application core. I wrote comprehensive tests against the expected behaviour. Then I rewrote the entire 'filtering' code to pass those tests.

Changing the DB structure next, was a breeze. With tests guaranteeing the sanctity of the core logic, I could rewrite & refactor like a madman.

Moral: When re-writing code, tests are the machine readable Bible of Specifications. Write them first. You'll thank yourself.

#3 You'll make love to your tests when you're deploying to production


After experimenting with InfiniDB & Infobright, my laptop refused to start plain-vanilla MySQL. So, I was forced to use an Sqlite3 DB for testing. Given ActiveRecord & it's DB abstractions, I wasn't expecting any issues deploying to MySQL in production (how naive?!)

Along came Murphy.

The very first comamand 'rake db:load' barfed. Did you know that MySQL does not allow TEXT columns to have DEFAULT values? I didn't. I quickly changed all TEXT fields to sufficiently large STRING fields and re-ran the test-suite. All clear.

Next, a perfectly innocent test failed against the production MySQL server. From the debugging output it was clear that an ActiveRecord association was unexpectedly becoming 'nil'. Digging deeper I realised that it was because of a special object that was required to have ID=0 in the DB. Did you know that MySQL will accept any value in an AUTO_INCREMENT INT field apart from 0? I didn't! I quickly changed the fixture, application code, & test code to look for ID=1 instead of ID=0 for this special object. Test. Clear.

From never having run the app against MySQL, to fixing stuff at last minute & still being confident of the app behaviour. I completed the entire deployment in 45 minutes flat! All thanks to those tests.

Moral: Most deployments don't go as planned. Many incorrect assumptions for the production environment are discovered at the last moment, resulting in dirty hacks & monkey patches. In such times, your test suite will be like your faithful lover, giving you confidence and standing by you through thick & thin.

Thanks to the "perfect-entry-for-The-Daily-WTF-code" written by the vendor I got the chance to experiment with TDD. I'm sold! Comprehensive test suites will be the baseline reqiuirement for all my future projects.

Update: Is it serendipity that the day I publish this blog post I get this Dilbert strip in my Google Reader? Hilarious!

4 comments:

  1. May I ask a stupid question. How do you 'write a test' in PHP?

    ReplyDelete
  2. @Gurpreet: Tests are just pieces of code themselves - just like the app/project being tested. The basic philosophy (with gross over-simplification) behind testing is that you call a bite-sized piece of code, say a function, with test data, and check that the function returns the expected value. If it does, the test passes, else it fails.

    (The test data is called a fixture in TDD jargon.)

    You can write a web-app in raw PHP or use a web-app framework. Similarly you can write tests in raw PHP or use a testing framework, which abstracts out the plumbing.

    For PHP testing take a look at PhpUnit or SimpleTest.

    ReplyDelete
  3. Awesome Nanda. :) Very nice. it will come handy. However do you have any blogs on how to achieve the TDD driven development/ implementation process? is it specific to development related projects?

    ReplyDelete
  4. @Shukla: What kind of projects, other than software development, are you referring to?

    ReplyDelete