Better Software - January 2008 - (Page 64) • Tests take significantly longer to maintain than system code. • Tests fail intermittently and inexplicably. • Tests are slow and are not run frequently. • The “defect-free” system crashes in production. It doesn’t have to be this way. TDD actually does work. It just takes a tremendous amount of discipline and an understanding of where the “landmines” are. This article applies a magnifying glass to the TDD fine print and suggests paths to safely navigate some key landmines. stories, which are ultra-lightweight, transient descriptions (see the StickyNotes for more on user stories). Stories direct the incremental development of functional tests, which starts a chain reaction of TDD cycles. The green cycle of one layer triggers the red cycle of the next layer. Notice the overall shape formed by these interconnected elements. Unit tests, like other things at the bottom of a food chain, are plentiful; coarser-grained functional tests provide context and direction. The linkage between all of these elements should be managed explicitly through grouping, organization, metadata, and intent-revealing naming schemes. • Control environmental factors, such as date and time • Completely isolate the system from its dependents • Trigger an action on the system • Control responses from dependent systems • Access all direct and indirect (side effect) outcomes Designing a system for this type of flexibility, controllability, isolatability, accessibility, and repeatability involves new design strategies. As Gerard Meszarus describes, decisions about how dependencies are managed, test environments are coordinated, and test data is handled will have longer-lasting implications than decisions about which automated tool to use (see the StickyNotes for more information). SOME ASSEMBLY REQUIRED The box for a train set contains a jumble of different pieces: segments of straight, curved, and yshaped track; train cars; a remote control; scenery; and more. A large part of the fun is putting the train set together in new ways. The “box” for TDD contains many interdependent pieces: unit tests, Figure 1: The full TDD cycle functional tests, user stories, user goals, business processes, iterations, releases, etc. All of DESIGN FOR these pieces support one another and TESTABILITY amplify the others’ power like interconDesigning and assembling a train nected gears. Like the train set, these track is not an end in itself; we want to pieces must be assembled prior to use. play with the train. Similarly, TDD is a As anyone who has had to assemble toys just a means to an end; we want to build knows, the ones with gears take the the right software (specification) and most patience and skill to get working. build the software right (confirmation). If “Red-green-refactor” is the TDD your team’s practice of TDD focuses mantra (see the sidebar), but it’s not the more on tests than on the software being complete story. The full TDD cycle starts developed, you are heading toward a with supporting a business process and landmine. The focus should be on how results in multiple layers of interconnect- TDD can help make the system’s design ed functional and unit tests as shown in more testable. A testable design facilitates figure 1. controlled experiments of the following Successful TDD isn’t adopted as an form: Given the system is in state x when isolated practice—planning and specifi- we do y, we expect the outcome to be z. cation activities are also impacted. To A highly testable system makes it easy assist in planning development iterations, for people and tools to: the system features that support the busi• Set the starting state for the system ness process are decomposed into user and all dependent systems 64 www.StickyMinds.com BATTERIES NOT INCLUDED Children are creative enough to play with power toys even when the batteries are missing. However, it is a more exhilarating experience with batteries installed. Similarly, teams following the basic red-green-refactor TDD rhythm typically end up with a heightened sense of confidence, clarity of purpose, and simpler code. Simply taking TDD at face value won’t magically result in highly testable systems or produce superior specification and design artifacts. To realize these kinds of long-term benefits, our batteries must be charged by investing time and energy in concepts like functional tests as effective requirement specifications and domain-specific testing languages. TESTS AS REQUIREMENT SPECIFICATIONS The most unfortunate thing about TDD is that the name contains the word “test.” On one level, it is a constant reminder of the radical reordering of process steps (test before you code). However, when the words are taken at BETTER SOFTWARE JANUARY/FEBRUARY 2008 http://www.StickyMinds.com
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.