Since the late 1990s when the software revolution moved into the fast lane, development shops have endeavored to find a way to operate within the demands imposed by strict budgets, fierce deadlines, and uncompromised quality. As marketing, testing, usability, and development departments all sought to bring an idea into existence, programmers were expected to function as a cog rather than as the wheel. As a result, the focus on a given project was scattered across business groups, placing ownership on the outcome nowhere in particular. An understanding of the original need that had inspired the project was lost in the push to get it done. The priority was to produce something that worked, to do it fast and within the deadline. Fulfilling the customer’s need or vision wasn’t a consideration.
The misplaced focus wasn’t intentional. Rather, it was a result of having too many cooks in the kitchen. Or maybe no one could tell the cooks from the chauffeurs. The point is, everyone was in the kitchen, and they were all making noise.
In 1999, Extreme Programming (XP) appeared on the horizon and, where it was adopted, everything changed. XP is a programming methodology that places developers in the center of the process. Actually, the customers are at the center, with developers embracing them through a team approach that incorporates a strenuous test-first philosophy. Test-First Development or Test-Driven Development (TDD), a core practice of XP, was quick to become a widely adopted practice.
“I think TDD is becoming very mainstream, not just in XP shops,” says David Vydra, producer of testdriven.com. He contends that the reputation of TDD has spread to such a degree many shops require a background in TDD as a condition of employment. In his current gig at Google, Vydra has found that TDD is quite popular, though not officially required.
TDD Stands On Its Own
In the short time it’s been around, TDD has branched off from its association with XP and is now seen as a practice by itself. Florida Tech teaches TDD without associating it with XP. John Roth, a Seattle area Java developer, explains that TDD was a piece of XP that was easy to extract and apply.
“XP requires the buy-in of functional business groups outside of dev,” says Roth. “TDD is a piece of it we can take with us and apply without needing the cooperation of anyone outside of the development team.”
TDD is also seen more and more as part of the fare offered with many other programming methodologies. Some of the programming methodologies that embrace the practice of TDD are:
- Rational Unified Process (RUP)
- Agile Unified Process (AUP)
- Open Unified Process (Open UP)
Not Your Usual Testing…
The term, TDD, might lead some to believe that it is another testing methodology,” James Bach, founder and operator of Satisfice, Inc., a company dedicated to teaching and offering Rapid Testing, cautions against thinking of TDD as a testing methodology. “Bear in mind that TDD is not really about testing,” Bach explains. “No one who is serious about the study and practice of software testing can take the tests of TDD very seriously. They are very weak tests. They are not produced for the purpose of aggressively finding bugs.” TDD is a software development methodology, setting a three-step cadence to each development phase.
As Figure 1 shows, the test is written first, before the code is written. The test covers something very small in the application functionality, perhaps the working of one method. Once the test is written, the code is written. The test is run with outcome of pass or fail, green or red. If it fails, refactor and try again. If it succeeds, write the test for the next chunk.and so on. Development within the context of TDD is meant to be fast and incremental.
Advantages of TDD
Developers list many advantages to working in this fast, incremental manner. Highest on the list of advantages to practicing TDD are:
- Quick results: Developers can see the effect of design decisions within minutes.
- Flexibility: Changes are easy because of the short distance between commits.
- Automatic catalog of regression tests: If something developed six months ago suddenly breaks under today’s code, it is known immediately.
- Good, clean code that works: As the mantra of JUnit testing proclaims, “If the light is green, the code is clean.”
Putting TDD into practice doesn’t have to be daunting. Many resources exist to help the novice. The tests used in TDD are automated unit tests, designed to validate that a unit or module of code is working properly. To support the creation of these automated tests, a unit test-writing tool exists for almost every programming language imaginable. Here are some:
|JUnit extension for database|
|.Net library for database projects||NDbUnit|
|Oracle Unit Tester||OUnit|
|Ruby||Test::Unit (Included with Ruby on Rails)|
Along with these test tools, programmers can find tons of resources to learn basic test writing. Most of the tools listed above are accompanied by a cookbook, giving detailed instruction on setting up these short unit tests. The JUnit tool for Java has the JUnit Cookbook, written by Kent Beck, founder of XP.
A Test in Action
The tests are meant to be simple and to test small bits of code. Below is an example of a unit test for a blog written in Ruby on Rails.
The blog has several fields, none of which can be null upon hitting the submit button. The test below will show whether or not the program demonstrates the expected behavior if the Title field is left blank. If the program is working properly, the submission will not be saved, and the program will display a generic error.
Expected Behavior: Program does not save record without title; reports errors.
Step 1. Write a test and confirm that procedure fails.
require File.dirname(__FILE__) + '/../test_helper' class BlogEntryTest < Test::Unit::TestCase fixtures :blog_entries # Replace this with your real tests. def test_title_validation entry = BlogEntry.new entry.text = "Some text" entry.title = "" # title is required. assert ! entry.save assert ! entry.errors.empty? assert (entry.errors.on "title").length > 1 end end
Step 2. Run the test, and see that it fails. Here’s the test’s output:
C:jcrhomeInstantRailsrails_appsfoo>ruby testunitblog_entry_test.rb Loaded suite test/unit/blog_entry_test Started F Finished in 0.203 seconds. 1) Failure: test_title_validation(BlogEntryTest) [test/unit/blog_entry_test.rb:13]: <false> is not true. 1 tests, 1 assertions, 1 failures, 0 errors
Step 3. Code the feature with the validates… line
class BlogEntry < ActiveRecord::Base validates_presence_of :title end
Step 4. And run the test again, seeing that it passes:
C:jcrhomeInstantRailsrails_appsfoo>ruby testunitblog_entry_test.rb Loaded suite test/unit/blog_entry_test Started . Finished in 0.125 seconds. 1 tests, 3 assertions, 0 failures, 0 errors
The Pitfalls of TDD
Practitioners of TDD say that the advantages of it become clear once the developer gets into it. But what about pitfalls? A couple of areas of concern are with applying TDD on multi-threaded code or where a working system with much legacy code is involved.
The FAQ on testdriven.com, written by Jim Dixon, mentions that there are practitioners who say that TDD can’t be used in developing multi-threaded code. Jeff Langr, author of several books on Java development, including Agile Java(TM): Crafting Code with Test-Driven Development, explains why multi-threaded code is a consideration.
With respect to multithreading: The core of the problem is that the
execution sequencing of multi-threaded code is non-deterministic.
Differences in things like current processor load, Java VM
implementation, and OS can mean that concurrent code interleaves
differently each time an application is executed.
In general, most developers have little experience in writing
thread-safe code. Understanding how to write tests against
multi-threaded code is even more difficult. The tests can be written,
but they generally require executing multiple threads on the test side
itself. There are a number of patterns that will help you write tests
for things like deadlocks and race conditions. These aren’t trivial
tools to build. It’s a significant investment, and most developers figure it’s not worth the effort.
TDD shows developers that small, single purpose classes are easiest to test.
Another area where one might hesitate to rush in with TDD is an environment where a large amount of legacy code exists. Langr says that it’s feasible to implement TDD in such an environment, but not without good cost. “If we never start to cover a system with unit tests, we won’t be able to improve its design much (via refactoring) without high risk. Almost by definition, our system will continue to degrade in
Langr says that he would introduce TDD by covering any new code with unit tests. “We won’t make the system any worse than it is,” he contends. Eventually, the code will be infused with pockets of tested areas. Langr recommends doing a cost analysis before attacking a project involving legacy code, the main consideration being whether or not the system will be around in six month’s time.
Scott Ambler, a software process improvement (SPI) consultant, trainer and mentor of a variety of development methodologies he has founded, has his own take on tackling the challenge of legacy code. Ambler feels that it is absolutely feasible to introduce TDD into a system with a considerable amount of history. “The strategy that you want to take is to introduce your tests as you touch the code,” Ambler says. The time to write the tests is whenever a procedure is updated. This way, the test suite to validate the system is built up incrementally over time. “If you have some legacy code that is mission critical, or that you’re afraid to touch because it’s very brittle, then you desperately need to start migrating towards a TDD-based approach for it.”
Overall, the practice of TDD is becoming so widespread that no developer can afford to avoid learning it. At the very least, a future job prospect or contract gig might come down to whether or not a given developer understands TDD. “Good, clean code that works” is an attractive promise. Software development can build a foundation with unit testing, whether using XP as a development methodology or plucking TDD from its crown.
Additional Resources and Further Reading
- Test-Driven Development by Example by Kent Beck
- Test-Driven Development, A Practical Guide by David Astels
About the Author
Nancy Corbett is a freelance writer, living in the greater Seattle area. She has worked as a UNIX Systems Administrator for small development shops and large wireless telecommunications companies for over ten years.
# # #