A QUICK SUMMARY – FOR THE BUSY ONES
TABLE OF CONTENTS
This article on Test-Driven Development (TDD) will help you become comfortable with this development cycle and adapt it into your coding methods.
The concept of Test-Driven Development (TDD) was introduced in 2003 by Kent Beck. There is no formal definition but Beck gives approaches and examples of TDD. The goal of TDD is to “write clean code that works”.
In TDD, follow only one rule of thumb: Only change the production code if any test fails. Otherwise, only refactor to optimize the code. For updated requirements, convert them to test cases, add these tests, and only then write new code.
TDD is a very short development cycle, and repetitive. Customer requirements are turned into highly specific test cases and software is written and improved to pass the new tests.
Test-Driven Development is related to test-first programming concepts in extreme programming, advocating frequent software updates/releases in short development cycles and promoting extensive code reviews, unit testing, and incremental addition of features.
A closely related concept to TDD is Acceptance Test-Driven Development (ATDD), where the customer, developer, and tester all participate in the requirement analysis process. TDD is both for mobile and web app developers, whereas ATDD is a communication tool to ensure that requirements are well-defined.
Let’s start with the basics and have a look at the TDD cycle, also know as Red-Green-Refactor process, step by step.
The Test-Driven Development cycle:
In TDD, every feature in a software is first added in terms of test cases. A test is created for a new or updated function. To write the tests, developers must understand the feature specifications and requirements.
This practice separates TDD from traditional software development methods where unit tests are written after writing source code. In this way, TDD makes the developer focus on the requirements before writing the code.
Running tests validates that the test harness is working correctly and simultaneously proves that as new tests added are failing with the existing code, new code is required.
The new code written in this stage may not be perfect and may pass the test in an irrelevant way. The only requirement in this stage is that all the tests should pass. One possible way to begin with adding the statements is to return a constant, and incrementally add logical blocks to build the function.
If all the tests pass, it can be said that the code meets the test requirements and does not degrade any existing features. If any test fails, the code must be edited to ensure that all the tests pass.
As the code base grows, it must be cleaned up and maintained regularly. How? There are a few ways:
Take small steps, targeting as few as 1 to 10 edits between each test run.
If the new code does not quickly satisfy a new test, or other unrelated tests fail unexpectedly, then undo/revert to a working code, instead of doing extensive debugging.
When using external libraries, it is important not to make increments that are so small that they merely test the library itself, unless it is to test whether the library is outdated/incompatible, buggy or not feature-complete.
In this part I’ll give you a quick TDD common practices walkthrough, that will help you code better.
A unit is a class/module that is a group of closely related functions, often called a module. Keeping units small adds benefits such as easier testing and debugging.
As you will always be running tests for units, it’s important to apply the following test structure:
Acceptance Test Driven Development (ATDD) has advanced TDD practices, and the development team has the target of satisfying acceptance tests that are defined by the customer. The customer may have an automated mechanism to decide whether the software meets their requirements.
For large systems, testing is challenging and requires a modular architecture with well-defined components. Some key requirements that must be fulfilled are:
In Scenario Modeling, a set of sequence charts is constructed, each chart focused on a single system-level execution scenario. It provides an excellent vehicle for creating strategies of interaction in response to an input.
Each Scenario Model serves as a set of requirements for the features that a component will provide. Scenario modeling can be helpful in constructing TDD tests in complex systems.
It is important to differentiate the code between testing and production. The unit test suite must be able to access the code to test. However, the design of criteria such as information hiding and encapsulation and separation of modules must not be compromised.
In object-oriented design, tests will still not be able to access private data members and methods and require extra coding. Alternatively, an inner class can be used within the source code to contain the unit tests. Such testing hacks should not remain in the production code. TDD practitioners often argue if private data should even be tested.
In this article, we got an overview of Test-Driven Development (TDD). We saw the benefits and limitations of TDD, and practices associated with TDD, covering ideas necessary to know in order to start adopting the TDD cycle.
Every year, Brainhub helps 750,000+ founders, leaders and software engineers make smart tech decisions. We earn that trust by openly sharing our insights based on practical software engineering experience.
A serial entrepreneur, passionate R&D engineer, with 15 years of experience in the tech industry.
Top reads this month
Get smarter in engineering and leadership in less than 60 seconds.
Join 300+ founders and engineering leaders, and get a weekly newsletter that takes our CEO 5-6 hours to prepare.
No previous chapters
No next chapters