Article

Home » Server-side Coding » Server Side Essentials » Test Driven Development: Are You Test-infected?

About the Author

Chris Corbyn

Chris Corbyn Chris is a freelance web developer from England, working in Melbourne, Australia. He specializes in PHP application development and is the sole author of Swift Mailer. Chris has worked for SitePoint during his time in Australia.

View all articles by Chris Corbyn...

Test Driven Development: Are You Test-infected?

By Chris Corbyn

June 20th, 2008

Reader Rating: 9.5

Page: 1 2 3 Next

When given a set of requirements to develop an application, most programmers can knock out something that works eventually, right? But all too soon after come requests for changes, and with those changes come the bugs. Perhaps they start small initially. They may even go completely unnoticed, but over time these small bugs begin to plague your application, and make you want to run away from the project screaming with your arms in the air. Yet it’s not fixing the bugs that’s usually the problem—it’s finding them in the first place. And as we know, a programmer can spend hours, perhaps even days trying to track down a bug that requires one line of code to fix.

Aside from the accumulating bug problem, you’re also faced with the potential for feature-creep as you add a little extra code here and there. Before you know it, your application has all these fancy bells and whistles that nobody will ever use, and you have a lot more code to maintain than the original specification intended.

Three years ago this was an all-too-familiar scenario for me. I’d heard people advocating the use of test-driven development (TDD) but put simply, I just never bought into the idea. But when I eventually got around to giving it a go for myself, I never looked back! This seems to be a regular occurrence among software developers. Generally we say people who took the leap into TDD and subsequently never develop without it are “test-infected”. Apparently there’s still no known cure!

Don’t get me wrong … learning to use TDD correctly is not easy. I got off to an incredibly bumpy start and very nearly gave up. My tests were breaking whenever I made changes to my source code and I wasn’t sure how to write many of the tests at all. It took a lot of concentration and the end result wasn’t nearly as elegant as I had been led to expect. I wasn’t the first and I won’t be the last. But, as when you learn anything for the first time, making these mistakes is what you often need to do in order to understand why you must do things a particular way. Three years on, I’m still learning how to write better tests but the tests I do write are a lot more flexible and very maintainable.

Introducing TDD

Let me tell you a little about my experiences with TDD, how it’s added to the enjoyment I get from writing software, and how the quality of my source code has improved as a result.

To explain the basics of TDD to those who have never used it before, we should perhaps think about the traditional form of testing. Traditionally, the three stages of software development are Planning, Implementation, and Testing. A developer would spend roughly equal amounts of time on each of these steps. The problem here is that although the plans seem bulletproof and the code may not be too difficult to write, the testing just happens far too late in the game. Bug reports get back to the developer after the software is already built, but obviously, with nearing deadlines, this is far from an ideal situation.

In reality, the process has never been quite as clear-cut as those three steps, however. Developers usually test small portions of their code once they’ve finished writing them; we call those small portions of code “units”. Whether you’re using the system from an end-user perspective, or you’re adding debug output to the code and emulating particular scenarios the software might encounter, this sort of testing gets tedious very quickly. There are only so many times somebody can perform the manual checking of values before they tire of repeating the process and simply stop. Traditionally, it’s unlikely that a developer would return to aspects of the system they’ve already written and perform that manual testing once again. As a result, small changes here and there are likely to introduce hidden bugs in other parts the system.

Test-driven development, on the other hand, goes against the grain a little. The process looks more like Planning, Designing, Testing, Implementation, Refactoring, Designing, Testing, Implementation, Refactoring … and so on, with far less emphasis on the planning phase as compared with traditional development. The Designing step is important. In order to write a test, the developers need to know what they’re testing. To do this, they’re constantly designing very isolated components in the system. This encourages a great deal of flexibility with the code, since readily testable code and flexibility often go hand in hand.

TDD usually starts with a unit test framework. That is not to say that test-driven development is unit testing—it’s not. Because I develop primarily with PHP, I chose SimpleTest, written by Marcus Baker. PHP offers another popular unit test framework called PHPUnit, but I opted for SimpleTest because it was the more mature of the two, and had more accessible support (largely due to the fact the author is a regular visitor here on the SitePoint Forums). Almost all unit test frameworks are extremely similar. JUnit—Java’s most widely used unit test framework—brought this methodology into the mainstream. Various frameworks written in other programming languages subsequently emerged more-or-less following JUnit’s minimal API. We call this group of frameworks the xUnit frameworks.

How do we test the system before we’ve implemented it, though? The philosophy seems flawed at first glance, but once the concept begins to sink in, it makes perfect sense. The tests themselves are written in code and make various assertions about the behaviour of the system under test (SUT). Writing the test is done inside a unit test framework such as SimpleTest. The idea is to write a small, simple test that expresses a behavioural requirement. For example, the SUT may be expected to return a negative value when passed a particular set of arguments. The unit test framework provides the means to make these assertions, and gives useful feedback to the developer if one or more of those assertions fail.

In TDD, the developer will write a deliberately failing test (since the SUT won’t have any implementation for the requirement yet), then go on to write the most obvious and simplest code to make this test pass. Once the test passes, the developer writes the next (probably failing) test, implements a little code to make it pass, and then moves on. The end result is that over time you have two large sets of source code—one is the test code itself; the other is the source code of the software. It’s highly probable that there will be more lines of code providing test coverage than there will be actual source code. There are several benefits of keeping all that test code in your project directory:

  • Running the tests ensures that the behavioural requirements they specify are still met.
  • They provide support to developers who wish to modify the software without breaking or changing its existing behaviour.
  • They provide a form of documentation for developers who need to understand how the software works.

All the xUnit frameworks provide mechanisms to group these tests together into a test suite and to run them all in one go, automatically. This makes the process of retesting the software far less cumbersome and much more appealing than all that traditional manual testing.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links