If it's difficult to get software developers to write unit tests, imagine how difficult it can be to get them to write the unit test cases before they write a line of code.
That's the principle behind test-driven development (TDD), a practice with roots in Extreme Programming (XP). According to advocates of TDD, the payoff can be big -- simpler and better designed code that delivers business value and has fewer defects.
However, TDD can also be a difficult practice to learn, and it requires a significant change in mindset on the part of the developer. As a result, adoption is not yet widespread and is primarily on an ad hoc basis, according to industry observers.
"I think test-driven development is an amazing practice, but I find it very uncommon," said Carey Schwaber, an analyst at Forrester Research Inc. "It's difficult to get people to do it. Unit testing is more common, but a lot of developers say they're doing unit testing but they're not really; it's either 'testing by developers' or they're exaggerating how much unit testing they do. Test-driven development is where you write the unit test case before you write the code, and writing the test case first is a lot harder. There are a lot of benefits, but it requires shifting your mindset."
"Thinking of what test to write will force you to improve your code," added James Shore, consultant, co-author of The Art of Agile Development and signatory number 10 to the Agile Manifesto.
TDD "helps define the expectations of the product, forcing engineers to do everything they commit to do and nothing more," said Christophe Louvion, CTO of Gorilla Nation, an online advertising sales representation company in Los Angeles. "For me, what it does is define better what the product is, removing a lot of the QA mentality like test later."
If engineering has ownership of TDD, he said, there's more time for QA to do exploratory testing and manual testing.
TDD for developers, not testers
Test-driven development, despite the "test" nomenclature, is a technique for developers, not testers, Shore said.
"It's used to ensure that what you're developing is what you intended to develop," he said. But "of all the XP practices becoming mainstream, this has a big learning curve."
Even for agile shops, adoption is still low, according to Schwaber.
"I would say a good number of agile shops are not doing test-driven development. In general, people who adopt agile pick up more practices over time. They eventually get to test-driven development, but it takes a while," she said.
How TDD works
According to Shore, TDD is a rapid cycle of very short steps, where the developer writes a small amount of test code, typically five lines or fewer, sees the unit test fail, writes a small amount of production code, sees the test pass.
"You're doing it to prove that what you intended to write is what you did write," he explained.
"There are a lot of advantages to having complete TDD coverage," Shore continued. "You can have a lot of confidence when you're making changes to code because the tests catch any mistakes."
Ryan Martens, CTO and founder of Rally Software, a developer of agile lifecycle management software based in Boulder, Colo., said he views TDD as a continuum, starting with writing unit tests prior to coding. The next step, he said, is acceptance test-driven development.
"Acceptance test-driven development focuses not on components, but it's an acceptance test for the story or piece of functionality," he said.
The third part is continuous integration and system testing, Martens said. Here is where a "stop-the-line" concept comes into play. If the integrated nightly build breaks, the team has to get it working again in the morning. "You're moving to a zero-defect mentality," he explained.
But just because a developer does unit testing or the team runs nightly builds doesn't mean they're getting the full benefits of TDD or even agile methods, Martens said.
"We find organizations where the developers don't write the unit tests -- the offshore teams write the unit tests. This is defeating the purpose [of TDD] because it doesn't allow you to think about simpler design," he said. However, "it allows you to refactor faster and get the unit test up."
And, Martens added, "if a highly technical team tends to be very focused on getting a nightly build process, that doesn't mean they're getting the benefits of TDD -- to think in terms of test and making a simpler design. It also doesn't mean they've picked up agile or done acceptance testing."
While unit testing is becoming more common if an organization has a language and tools that support it, Martens said, "it's pretty easy to write really dumb unit tests." And while most organizations say they run unit tests, "most don't have coverage above 30%. It's mostly still ad hoc; that's where the industry is," he said.
Getting started with TDD
Shore said organizations that contact him about TDD typically have an increasing testing burden as applications continue to grow more complicated, so they're looking to move to shorter cycles, such as those when using Scrum, he said.
"It's risky to go for a year of development without seeing delivered software. If they're still throwing it over the wall to testers, the testers are finding the test burden going up," he said. "As the number of features increases, regression testing increases. When it becomes a problem, they call me. They want to deliver software without a large regression burden."
To get going with TDD, Shore recommends starting with new development and starting something small in order to get a win.
"TDD is for writing code, not modifying. More than anything, test-driven development gives you a rhythm, a smooth flow of writing test, code, refactoring," he said. "It should be natural, but it's hard to get there; thinking of what code to write and stepping back to how you can test it. Choose a simple problem that doesn't involve a UI or a database."
For Gorilla Nation's Louvion, who is in the process of changing the shop's methodology to Scrum and uses Rally's agile lifecycle management software, starting to do TDD raises a lot of engineering practice problems that you have to resolve.
"To do TDD in my mind, you must have continuous integration," he said. "My former team [at a previous company] was very talented in doing test-driven development. When the story was too fuzzy and they wouldn't know what to test, it helped them define it faster. From my experience [TDD] is helping to better define the scope of stories, which is a driver for better quality."
Louvion said he is implementing TDD with just one team to start and to make the case that TDD works. "Also, it is more involved in terms of knowledge, and a more profound change. It's a more organic implementation of TDD," he said.
Be prepared to go slow at first when starting out with TDD and to go fast later, practitioners warn.
"It's certainly a change process," Martens said. "People can try to bite off too much too fast, but it doesn't have to radically slow things down. But if you're learning new stuff, it will have to take in some overhead."
However, TDD is critical to releasing more often and quickly, he added. "In our team we run eight-week release cycles. Over 27 releases have stopped in the middle and shipped. You never have that option if you run in waterfall, with testing on the back side."
For a new system, Shore said he finds TDD takes about two months and 200 tests on average.
"I can't say that's true for everybody, but it takes that amount of time [typically] to learn and create a test helper library and understand how to modify design to make testing easy," Shore said. "Once you've gotten over the hump, and you've got libraries and support in place, it starts being very fast. This is partly because I'm not debugging anymore, and I get into a rhythm where I constantly know where the next step is."
However, Shore added, for legacy code and adding test to a code base, it can take time. That's because it's a lot harder to do TDD on legacy systems. Because of that, he recommends learning on a green field.
Another recommendation is to not force TDD on developers, Martens said.
"If you roll out TDD, the last thing you want to do is to make it mandatory. You will get people writing stupid unit tests that are hardly tests, and you will get subversive behavior," he said.
Martens suggested letting the team agree to try TDD, with the ability to stop if they find it's not working.
"It's more about process change and testing forward in the cycle and making it the responsibility of developers," he said. "In organizations where there's a hard wall between QA and development, there's a lot of scary stuff in there [with TDD]. For organizations like that, the best approach is in an agile, lightweight fashion."
That means asking the team what they think and if they want training. It's more of a grassroots approach than a top-down approach, he said.
Forrester's Schwaber said she has seen ad hoc adoption by individual developers.
"Developers can do TDD even though their colleagues are not. It makes it harder to drive [through the organization], but it really is individual to the developer."
Question of scale
With any technique, there's always a question around the ability to scale for large projects, but practitioners said that's not an issue with TDD.
"I've never found any limit with TDD in terms of scale," Shore said. "It operates at the level of individual class, so it scales just as well as software scales -- both perfectly and not very well, depending on what you're talking about. It should be as much a part of the routine as compiling code or typing on a keyboard. Those things become slower as a system gets bigger."
It is the same thing with TDD, he said. "It's not test-driven development slowing you down; it's the overall size of the project."
Martens agreed: "I don't think there are any scaling issues. It's good for small projects, and it's almost required for big projects. When all teams are on the same iteration release calendars, the teams have dependencies. If there are defects you introduce that break the build, other people can't do their stuff and it slows the whole program down."
Practitioners are quick to point out, however, that TDD is no panacea for quality.
"You can create good software quality without test-driven development, and you can create bad software with test-driven development," Louvion said.
Shore summed it up this way: "It won't make a poor designer magically great, but it will help competent designers design better."