Expert articles on best software developments practives
Get a bi-weekly email with most popular stories
Jan 2, 2021
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:
1. Add a test, which will certainly FAIL. (Red)
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.
2. Run all the tests. See if any test fails.
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.
3. Write only enough code to pass all the tests. (Green)
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.
4. Run all the tests. If any test fails, go back to step 3. Otherwise, continue.
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.
5. Refactor the code. (Refactor)
As the code base grows, it must be cleaned up and maintained regularly. How? There are a few ways:
6. If a new test is added, repeat from step 1.
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.
Published on May 06, 2021
Published on January 6, 2021
Published on January 4, 2021
Published on March 12, 2021
Get a bi-weekly email with most popular stories