Part 1. A TDD primer
Part 1 is a test-driven development (TDD) primer, giving you a kick start in the art of test driving. In chapter 1, youâll learn about both TDD and its big brother, acceptance TDD, from the very basics, getting an overview of both techniques.
Chapter 2 takes you deeper into the test-first realm through a hands-on tutorial that you can follow on your computer, editing and
running actual code as we go along. Chapter 3 continues on this path, developing the hands-on example further by throwing in a larger-scale refactoring that introduces
significant changes to our design.
While teaching TDD to dozens and dozens of programmers over the years, Iâve learned that practice is a better teacher than
I am. By the time youâve implemented a fully capable template engine through chapters 2 and 3, youâll be ready to add some heavily guarded trade secrets to your toolbox. Chapter 4 expands our idea of TDD with a number of tips and tricks, from selecting the next test to different ways of making it pass.
Design guidelines and testing tools will get the coverage they deserve, too.
Chapter 1. The big picture
I can stand brute force, but brute reason is quite unbearable.
Oscar Wilde
âOnly ever write code to fix a failing test.â Thatâs test-driven development, or TDD,[1] in one sentence. First we write a test, then we write code to make the test pass. Then we find the best possible design for what we have, relying on the existing tests to keep us from breaking things while weâre at it. This approach to building software encourages good design, produces testable code, and keeps us away from over-engineering our systems because of flawed assumptions. And all of this is accomplished by the simple act of driving our design each step of the way with executable tests that move us toward the final implementation.
1 The acronym TDD is sometimes expanded to Test-Driven Design. Another commonly used term for what we refer to as TDD is Test-First Programming. Theyâre just different names for the same thing.
This book is about learning to take those small steps. Throughout the chapters, weâll learn the principles and intricacies of TDD, weâll learn to develop Java and Enterprise Java applications with TDD, and weâll learn to drive our overall development process with an extension to the core idea of TDD with what we call acceptance test-driven development (acceptance TDD or ATDD). We will drive development on the feature level by writing functional or acceptance tests for a feature before implementing the feature with TDD.
As a way of applying tests for more than just verification of the correctness of software, TDD is not exactly a new invention. Many old-timers have stories to tell about how they used to write the tests before the code, back in the day. Today, this way of developing software has a nameâTDD. The majority of this book is dedicated to the âwhatâ and âhowâ of test-driven development, applied to the various tasks involved in developing software.
In terms of mainstream adoption, however, TDD is still new. Much like todayâs commodities are yesterdayâs luxury items, a programming and design technique often starts as the luxury of a few experienced practitioners and then is adopted by the masses some years later when the pioneers have proven and shaped the technique. The technique becomes business as usual rather than a niche for the adventurous.
I believe that mainstream adoption of TDD is getting closer every day. In fact, I believe it has already started, and I hope that this book will make the landing a bit less bumpy.
Weâll start by laying out the challenge to deliver software using the current state of the practice in software development. Once weâre on the same page about what weâd like to accomplish and whatâs standing in our way, weâll create a roadmap for exploring how TDD and acceptance TDD can help resolve those problems, and weâll look at the kinds of tools we might want to employ during our journey becoming to master craftspeople.
1.1. The challenge: solving the right problem right
The function of software development is to support the operations and business of an organization. Our focus as professional software developers should be on delivering systems that help our organizations improve their effectiveness and throughput, that lower the operational costs, and so forth.
Looking back at my years as a professional software developer and at the decades of experience documented in printed literature and as evidenced by craftsmenâs war stories around the world, we can only conclude that most organizations could do a lot better in the task of delivering systems that support their business. In short, weâre building systems that donât work quite right; even if they would work without a hitch, they tend to solve the wrong problems. In essence, weâre writing code that fails to meet actual needs.
Next, letâs look at how creating poorly written code and missing the moving target of the customerâs actual needs are parts of the challenge of being able to deliver a working solution to the right problem.
1.1.1. Creating poorly written code
Even after several decades of advancements in the software industry, the quality of the software produced remains a problem. Considering the recent yearsâ focus on time to market, the growth in the sheer volume of software being developed, and the stream of new technologies to absorb, it is no surprise that software development organizations have continued to face quality problems.
There are two sides to these quality problems: high defect rates and lack of maintainability.
Riddled with defects
Defects create unwanted costs by making the system unstable, unpredictable, or potentially completely unusable. They reduce the value of the software we deliverâsometimes to the point of creating more damage than value.
The way we try to get rid of defects is through testingâwe see if the software works, and then we try to break it somehow. Testing has been established as a critical ingredient in software development, but the way testing is traditionally performedâa lengthy testing phase after the code is âfrozenââleaves much room for improvement. For instance, the cost of fixing defects that get caught during testing is typically a magnitude or two higher than if weâd caught them as they were introduced into the code base. Having defects means weâre not able to deliver. The slower and the more costly it is to find and fix defects, the less able we become.
Defects might be the most obvious problem with poorly written code, but such code is also a nightmare to maintain and slow and costly to develop further.
Nightmare to maintain, slow to develop
Well-written code exhibits good design and a balanced division of responsibilities without duplicationâall the good stuff. Poorly written code doesnât, and working with it is a nightmare in many aspects. One of them is that the code is difficult to understand and, thus, difficult to change. As if that wasnât enough of a speed bump, changing problematic code tends to break functionality elsewhere in the system, and duplication wreaks havoc in the form of bugs that were supposed to be fixed already. The list goes on.
âI donât want to touch that. I...