A case for Test Driven Development

Thursday, March 25th, 2010

I will be honest: TDD is hard. It’s hard to get into, it’s hard to keep up. It’s all too easy to revert to a time when programming was about creativity, experimentation and the gratifying feeling of printing ‘Hello, world!’.

There are other problems with TDD. Not good ones, but certainly the kind which influence one’s day-to-day:

  • Mistakes can be fixed, especially if you work online
  • It’s extra code you have to maintain when your boss decides to “take the product in a new direction”
  • Having to justify your time to the people paying you
  • Human testing is more versatile and it creates jobs (the US is in a recession, after all)

However, my biggest issue with TDD is the difficulty in setting up the test environment. Separate database, client simulator, 3rd party integration, sample data. I can’t remember the last time I had sample data before beginning to code.

So then why work with TDD at all? For me, it is becoming a way to manage code which I know I will forget about in two weeks. Perhaps you’ve experienced this before: revisiting code you know you didn’t write, despite an SVN commit claiming otherwise. I’m talking about legacy code. And if you’re working alone on a growing project, legacy is last Monday.

And what of the environment issue? If you happen to be using Django, you’re in luck. Take the following code sample:

# player/tests.py
import unittest
from django.test.client import Client
from django.core.urlresolvers import reverse
from models import Player
 
class PlayerTest(unittest.TestCase):
    def setUp(self):
        self.client = Client()
    def testRegistration(self):
        data = {
            'username':  'John Doe',
            'password1': 'password',
            'password2': 'password',
            'email':     'john.doe@eservice.ca'
        }
        response = self.client.post(reverse('register'), data)
        assert response.status_code == 200
        assert Player.objects.filter(username=data['username']).count() == 1
    def tearDown(self):
        Player.objects.all().delete()

And to run the test, call python manage.py test player from the command line.

The method testRegistration() submits the data for a registration form using Django’s client simulator. In return, it expects a status code of 200 (success) and a player record of username ‘John Doe’. Once this test passes, I can be sure that at least some of my code is being checked for trouble. With a few other tests, in the above class, I can guarantee that new customers can, at the very least, register for the application.

I can also justify not writing documentation: read the tests.

The example I have given is a long stretch away from serious Test Driven Development. I may also slip and “forget” to test a rushed bit of code. But a developer has to start somewhere, so why not with a ‘Hello, TDD!’

Leave a Reply