You probably know the importance of software testing if you've been a developer for any length of time. Simply put, effective testing catches defects. While testing can't prevent all bugs, it can help reduce their number.
But exactly how should you go about testing your code? This question has led to a number of methodologies and practices. One popular approach is test-driven development (or TDD). At a basic level, TDD is an approach to testing/software development that involves writing tests before coding.
Today, we'll cover an overview of TDD and the advantages and disadvantages of using this approach.
We'll cover:
- Test-driven development: An overview
- TDD advantages
- TDD disadvantages
- Related development approaches
- Wrapping up and next steps
Test-driven development: An overview
TDD is related to principles of extreme programming, but it has also gained popularity in different contexts. Extreme programming is a type of agile software development intended to improve software quality and responsiveness to changing requirements. Kent Beck, who developed extreme programming in the late 1990s, says he rediscovered the test-first approach from an old programming manual. He later wrote his own book on TDD: Test-Driven Development: By Example.
To provide a definition, TDD is a technique in which you first write a test case, then develop the smallest portion of code needed to pass that test. This is contrary to typical development models in which feature development precedes test case creation. One notable feature of TDD is that it builds testing directly and prominently into the software development process, ensuring that the smallest portions of code within a piece of software work as expected.
The TDD process: From test cases to refactoring
We can view TDD as an iterative cycle with several steps:
- 1. A developer writes a test for a new feature based on specifications.
- 2. The developer runs all existing tests. The newly created test should fail because the code for its feature hasn't been written yet. This validates that the test was needed.
- 3. The developer writes the simplest code needed to get the test to pass.
- 4. The developer refactors the new code to improve the code’s readability and maintainability.
- 5. The developer repeats the process, eventually collecting new tests for every intended feature of the code.
Refactoring and other TDD principles
Refactoring is a key step in TDD. It involves improving the internal structure of source code while preserving its functionality. Note that in the TDD cycle, refactoring occurs after a test passes. A failing test would require rewriting the code to pass the test. Refactoring shouldn't be confused with fixing bugs, simply rewriting code, or improving observable aspects of software like the UI.
According to TDD, refactoring should continue until the source code conforms to the simplicity criteria. Developed by Beck, these criteria are intended to determine whether some amount of source code is "simple enough." These criteria are also known as the rules of simple design and are listed in order of priority:
- The source code is verified by automated tests, and all such tests pass.
- The code contains no duplication.
- The code expresses separately each distinct idea or responsibility.
- The code is composed of the minimum number of components (e.g., classes, methods, lines of code) compatible with the first three criteria.
Beck is far from the only person to contribute to TDD conventions. The software engineer Robert C. Martin has distilled the approach further, summarizing his ideas in the three rules of TDD:
- "You are not allowed to write any production code unless it is to make a failing unit test pass."
- "You are not allowed to write any more of a unit test than is sufficient to fail," and failures include both compilation and runtime failures.
- "You are not allowed to write any more production code than is sufficient to pass the one failing unit test."
These rules are designed to ensure all production code is written in response to test cases. It seems simple enough, but TDD has critics as well as supporters. Next, we'll look at the advantages and disadvantages of the test-first approach.
TDD advantages
If the point of software testing is to reduce bugs, TDD should be good at this, right? According to the Agile Alliance, a nonprofit focused on the Agile Manifesto for Software Development, many teams practicing TDD do report a reduction in defect rates in production code. Of course, this reduction comes with some overhead costs for the initial development effort. But these same teams say the overhead costs are offset by reduced effort in the final phases of projects.
In addition, veterans of TDD say the emphasis on refactoring leads to better design quality in source code and a higher degree of internal or technical quality. That said, empirical research hasn't backed up this claim.
IBM identifies other advantages of TDD:
- Because the resulting code is robust, TDD can enable faster innovation and continuous delivery.
- Code is flexible and extensible. This means code can be refactored or moved with little risk of breaking the code.
- Tests themselves are tested, with developers verifying that each new test fails as part of the TDD process.
- The resulting code is easy to test.
- There's little to no wasted effort because you only write code for the feature needed to satisfy your requirements.
IBM has also shared a TDD anecdote on its website about an experiment one of its development teams conducted. Some of the team members used TDD, while the rest wrote their tests after completing their code. When the developers in the second group were done with their code, they were surprised to see that the TDD coders had finished earlier and had what they considered more robust code. It's just one example, but it certainly seems to support the benefits of TDD.
TDD disadvantages
The test-first approach doesn't work for everybody. The Agile Alliance identifies some common pitfalls.
Individuals may:
- Forget to run tests frequently
- Write too many tests at once
- Write tests that are too large
- Write tests that are overly trivial
- Write tests for trivial code
Teams may:
- Use TDD inconsistently
- Fail to maintain suites of tests, leading to overly long run times
- Abandon TDD test suites due to overly long run times or team turnover
These pitfalls may be more the result of not following TDD best practices rather than flaws in the approach. But search the web and you'll find other criticisms of TDD. One is that TDD can lead to disregarding large- or medium-scale design because software design is so focused on the feature level.
In addition, detractors often say that TDD is an ideal that's not suited for real-world problems in software development. These problems might include:
- Large, complex codebases
- Code that must interact with legacy systems
- Processes running under stringent real-time, memory, network, or performance constraints
Related development approaches
While popular in its own right, test-driven development has inspired offshoots that aim to further improve upon the software development process. We'll look at two of these.
Acceptance test-driven development (ATDD)
ATDD is analogous to TDD, but with a focus on the acceptance testing process. Acceptance tests are formal descriptions of how finished software products should behave. These usually take the form of examples or usage scenarios. In ATDD, team members involved in acceptance testing represent three stakeholders: customers, developers, and testers.
One benefit of ATDD is that acceptance tests represent the end user's point of view. Additionally, ATDD tests serve as clear requirements that describe how programs will function. And they also ensure that programs function as expected.
Behavior-driven development (BDD)
BDD combines and refines practices of TDD and ATDD, with a focus on making sure products serve business objectives. This approach scrutinizes proposed user stories to ensure that a test's purpose is clearly related to business outcomes.
What are user stories? A user story is a functional increment of how a software development team's work is divided up. Teams often write user stories in consultation with customers or product owners.
BDD may offer several benefits. It can minimize waste by implementing only the product behaviors that contribute to desired business outcomes. Additionally, BDD describes behaviors in a single notation accessible to domain experts, developers, and testers, and this simplification can improve communication. BDD also offers more guidance than TDD on organizing communication between domain experts, developers, and testers.
One caveat for BDD: it's considered best for teams already using TDD or ATDD because it requires familiarity with a wider range of concepts.
Wrapping up and next steps
At this point, you should feel comfortable with the principles of test-driven development, its advantages, and its disadvantages. Where you go from here likely depends on your role. If you're an individual contributor, you may find implementing TDD difficult without support from your team or organization. If you're an engineering leader, you may be in a better position to put TDD into practice if you find it compelling.
Wherever you land on TDD, it's important to ensure you incorporate robust testing into your development practices. Whether it’s unit testing or regression testing, it will help make sure your code functions properly, meets specifications, and doesn't introduce defects at the production level. If you decide to try TDD, it certainly offers advantages, and to do it well, it helps to master writing tests.
If you work with Java and want to improve your unit testing skills, you might consider learning JUnit. To help you master this topic, we have developed the course Unit Testing Java Applications Using JUnit 5. It covers the fundamentals of unit testing and JUnit 5, as well as core concepts like assertions and assumptions, and advanced concepts of the JUnit framework.
Happy learning!
Continue learning about software testing
- All about unit testing: 11 best practices and overview
- Software testing 101: Get started with software testing types
- Performance testing tutorial: Automation, Gatling, and Jenkins
Start a discussion
What do you think about TDD? Was this article helpful? Let us know in the comments below!
Top comments (1)