Test Driven Development, a Portable Methodology
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