Positives of Test Driven Development (TDD)
I want to demonstrate some of the good aspects of Test Driven Development (TDD) Unit testing I can see using it as a methodology for software development.
TDD can be used at high level to test new features of the site (acceptance tests) and the integration of classes within the system (integration tests). Classes like the one shown below.
In this post I'm displaying most of the process of unit testing the class Gravitywell_Validate_InSchedule which is a custom validator for use with the Zend Framework (ZF). It's used to check a date-time variable is within a schedule of two other date-time variables. I say most of the process as I'm focussing on talking about positive outcomes from using it.
Part of the TDD mantra is to start with a failing test and grow the software by writing the software that passes that test. I'm using SimpleTest as the testing framework to build my tests, the goal being to develop software that is durable against the test of time and flexible to changes in requirements.
There is some preparation to using SimpleTest to start testing/producing software. You can follow this link see a tutorial of how to do that.
I'll start with the most simplistic failing test I can think of for this class. It's important that this class extends Zend_Validator_Abstract so it can be used within our FW app. All new features of development using TDD uses a failing test to start with.
Test 1: Assert that this class is an extension of Zend_Validator_Abstract.
The function name here is quite long but you only ever have to type it once. It's important that the name is as clear as possible because it might be used by another programmer to see what was done. It might also be used in documentation. A side benefit of righting failing tests for a project before source code is that the process of development becomes self documenting.
Using SimpleTest I can see that this test passes. Another way of building the test would be to use a mock object functionality of SimpleTest to mock, by means of reflection, the class I'm extending and confirm the all the class methods have been implemented in my class. You can download the finished class Gravitywell_Validate_InSchedule here.
Next I want to prove my class is setting the date-time property correctly. The test will be done against a unix timestamp but the user may enter the value in any format. So the date-time entered must be converted.
Test 2: Confirm that setting the date resolves to a unix timestamp.
I ended up creating 6 new tests from this test, 3 methods for the setting the start date and 3 for the setting the end date. This is because I wanted to allow the user to enter date-times in timestamp format, strtotime() format or Zend_Date format.
I've now added the following test functions to the TestInScheduleClass class:
There was obvious duplication in the system. Apparent even in the paragraph above. So I re-factored the code into a function that is called from 3 places in this class
This refactor also removed the noise of converting the date from the isValid method making the method in question much more clear about its functionality. Which in turn makes it clearer to the programmer.
You can see evidence of self documentation growing with the tests. A new programmer now knows the class accepts Time; MySQL date-times; and Zend_Date objects just from looking at the tests defined. A note on documentation creation from tests is described here.
Another positive that came from developing the class using TDD was that the test fails on errors such as when I copied and pasted code for setting the start date to setting the end date. Where I forgot to search for the word start and replace for the word end. This was better that assuming I'd written the class correctly only to have to track the error from something I would have seen in the front end of the site. From there error might not have been obvious.
A good book on the subject of TDD can be found here.