As software development approaches have evolved over the past two decades, various agile models have gradually taken hold. Since software development needs are rapidly changing, best practices have evolved, moving progressively toward continuous delivery and accelerating time to market.
The test-driven development (TDD) practice has gained popularity because it works to resolve the speed vs quality dilemma once and for all. As part of the agile software development niche, Test-Driven Development (TDD) ensures that quality comes first, and it is one of the newest approaches.
What is test-driven development?
The main idea of this methodology is to write tests to determine and verify what the code should do. In other words, it is the practice of forming a test before coding a piece of functionality. When an automated test fails, developers must write new code. Therefore, this idea allows developers to spend more time analysing the final product's design or requirements before writing the code.
Why You Should Use TDD
Using TDD for software development is a more balanced approach emphasising three primary tasks: coding, testing, and designing. Coding and testing are built for one use case at a time, not for an optimal solution. As a result, software development can become more efficient and smooth by delivering feedback more rapidly and encouraging developers to write clean, solid code.
Speeds up development. As developers get used to writing tests, things will get slower. Once you get used to it, development speeds up;
High coverage of functional tests. For each feature, I write 20-25 tests to cover all possible use cases;
Less time for debugging. Usually debugging starts when the main functionality is already done. Here we are debugging the concept of functionality in advance while writing tests.
How it works
The path of any code in the development of TDD goes through 3 stages:
Red stage: writing tests. The first test run will always return an error because the interface they are testing is not ready. Each test should experience at least one fail in its life. At least right after writing. As the TDD proponents say, "I can't trust a test that I've never seen fail." A failed test will draw attention to itself, and you will think about the correct implementation of the functionality or the test itself.
Green stage: we develop the functionality and re-test it. Our task is to ensure that the test turns green. So we understand that the functionality is developed correctly. As I said before, it is important that the test fails at least once and passes at least once. It's easy to make the mistake of writing a test that always fails or always passes. But the test, which both fails and passes, clearly checks some kind of logic. After that, we can assume that the functionality works correctly.
Refactoring. We refactor a particular task since we are certain that the test code is already functional. We just write logical and clean code for our project.
Where does it apply
The choice of TDD depends on:
Teams and a person who sets and accepts tasks (let's call him the customer).
Internal processes on the project. For example, the team does not use continuous integration. You should not use TDD here, because the developer will not run tests every time before uploading the code to the repository. He or she will get lazy eventually.
There are criteria by which you can determine that TDD will work:
The project clearly describes the structure of interfaces, for example, JSON-API or REST-API are used. Our goal is to speed up product development. When there is a clear structure, the programmer does not invent the structure himself. If there is no clarity, the programmer thinks over the structure of the interfaces, and his vision may not coincide with what is in the head of the customer. Therefore, if the project has a well-thought-out API structure, write tests before the code;
The project has a similar approach to the implementation of interfaces, i.e. the logic and order of work on different entities is the same or very similar. Before writing tests, we organise the required data in the test environment. If the project has a certain approach to the implementation of interfaces, you can safely apply one pattern and save time writing tests. If there is no single approach in development, you will have to create your own conditions for each test before launching. This will take the same amount of time as normal development, so using TDD will not bring any benefit;
Tasks are set in the style: one subtask - one test. Usually such tasks are set by the customer-technician. Start the system: inside a task, each subtask is one test. Then the developers calmly write tests that describe subtasks, then the implementation, and the tests gradually become green. If several tests can be isolated in each subtask, the process will not gain in time compared to conventional development;
There is a high-level test framework with support for integration tests on the technology used in the project. Alternative option: a test framework abstracted from the implementation of another technology. All my TDD is Laravel projects. Here I use PHPunit testing tool. Tests should be abstracted from the implementation, so it doesn't matter which tool you choose. It is better to use a tool that exists in your language. This way the programmer doesn't have to switch context during development;
There are no individual testers in the team. If there are testers in the team, they should write the tests. As a result, the main advantage of TDD is lost - “the mental connection of the programmer with the functionality”, because the tests are written by another person. For TDD to be useful, tests must be written by a programmer.
TDD is a must if a programmer writes an API that returns JSON to the frontend. If a programmer writes an API without TDD, checking JSON just with “a look” takes a very long time. The longer it checks, the more errors and the longer it takes to fix them. When tests are written, they automatically check JSON for errors. Pre-written tests will help the programmer validate the JSON output. As a result, development goes faster.