Go to
At least a few significant applications have been created that I know about, including warehouse management programs supporting the operation of stores, financial institutions and banks. However, over the last 15 years, we have observed more emphasis being put on tests, and the old approach is slowly becoming history in a development sense. So let’s move on to the present day and take a look at Test-Driven Development, an approach which is the ‘state of the art’ of modern software testing. Why is it worth adopting TDD?
- Test-driven development (TDD) promotes test-first development. The programmer tests a function before he writes the code.
- TDD focuses on delivering thoroughly tested software solutions. It is an approach to development which combines Agile methods with a comprehensive test suite.
Read our Guide to Agile software development
TDD vs Agile
The TDD approach is one of the agile techniques of software development. The Test-Driven Development technique, introduced by Kent Beck, one of the authors of the Agile Manifesto, involves repeating the Red – Green – Refactor cycle repeatedly. What distinguishes the Test-Driven Development technique is the fact that you test before you write a function, meaning you write tests for new features that have not been developed yet.
Also read:
What is Test-Driven Development (TDD)?
Using TDD allows you to add any new functionality to the developed application, minimizing the risk of accidental errors in unmodified areas of the program, which we could forget about, e.g., after a few months. One of the advantages of Test-Driven Development is that by modifying or adding functionality, we automatically receive reports on all related areas that are affected by our changes. This gives you an opportunity to quickly improve them and prevent any errors being introduced along with functionality. The Test-Driven Development approach allows you to design advanced patterns and makes designing applications legible and transparent thanks to automation. It works particularly well for complex projects. A great deal could be written about Test-Driven Development. In this article, I would like to focus on the use of TDD in practice and the benefits of this approach.
Years ago, when I was taking my first steps as a programmer, I joined a project in which my colleague and I were tasked with creating a dedicated configurator for a window manufacturer. The mere fact of the technological complexity of window production should determine maximum caution. The user of the application could configure the window based on both standard and non-standard parameters. Depending on the dimensions, certain options were either available or unavailable. The user had to follow several steps with numerous possibilities. The final price was influenced by various options and factors.
In this project, I observed at least a few disputable practices, e.g., no testing code or code review. However, the manager and the client did not know that wherever possible, we tried to review our work, the effects of which were intended for use directly in production.
Throughout the project, I was under significant pressure. I did not argue when I was told that unit tests are not carried out in PHP (which we were writing in). As a result, the code, which we developed after modifications, required multiple corrections. If we had used Test-Driven Development at that time, we could have avoided a great deal of frustration connected with endless improvements.
So how does TDD work?
Test-Driven Development Diagram
The Test-Driven Development (Red – Green – Refactor) diagram seems to indicate something very simple. Let’s look at its individual elements.
- Red – this is nothing more than the stage of writing unit tests for functionalities. Running on the application without the implemented solution, it will inevitably generate an error report, indicating, at best, the incorrect functioning of the application in this area (that is, a lack thereof). In most modern programming tools for writing code, such a report will be colored red. This stage is very important. This is when the developer recognizes the problem he is working on, analyzing the existing code that describes this area (if there is one). It is also at this stage that he will shoulder the greatest burden during the implementation of the new functionality.
- Green – this is the next stage, in which the red from subsequent reports after running unit tests will progressively move to green. IDE (Integrated Development Environment) will signal that the test passes. At this stage, the proper implementation of the new functionality will take place.
- Refactor – at this stage, we introduce improvements to the newly implemented functionalities. We are also able to share similar solutions used in other places. For proper implementation, we clean the code and improve prepared test cases. This phase does not mean the end of the tests, because after further modifications of the code, the Red – Green – Refactor cycle repeats.
Also read: RxJS in JavaScript
Test-Driven Development – can you start with the Green phase?
Of course, we can achieve similar results starting with the Green phase, via Red, to Refactoring. As there is a decent chance to implement the application before we even prepare sets of unit tests, such a path even seems quite tempting. However, there is a catch to this approach. While dealing with subsequent functionalities, we may begin to pay less and less attention to the quality of test scenarios. I’m not saying it will happen for sure, but sooner or later our testing code may not cover the possible scenarios or may even become worthless. This increases the risk of serious errors in the production – errors that are difficult to repair and are most often found in rarely used areas. This often results in considerable expense on the client’s side related to fixing the errors.
Write a test – advantages of starting with the red phase
There is a lot of discussion about the fact that by starting the functionality cycle from preparing unit tests, we will need more time for the task than when we start work from the implementation stage. This may be true when a programmer starts working on a new project and has to prepare everything from scratch. Then you may need to create additional classes to support the creation of test cases or implement one of the available template engines to generate requests or events. Later in application development, if there are any differences, they are rather at a marginally low level.
Minimizing the number of tests in a development process
Shouldn’t we create test scenarios for everything, even the smallest bit of the code? Even the smallest elements are components of some larger structures and there is usually a chance that they can be described together using several reasonable scenarios of edge cases. Of course, there are areas like validators or calculators, which are often impossible to examine and cover with a small number of cross-sectional tests. However, I would like to suggest how to minimize the number of tests to save time and use the full potential of TDD on a daily basis.
Test coverage and non-existent business cases
An interesting issue is how to cover non-existent business cases with tests. We most likely won’t face such a dilemma creating tests for validators or filters, but it may occur when it comes to writing tests to check how a created functionality works. These are usually situations that, for some reason, will never happen – e.g., when our functionality depends on data from strictly defined sets.
Selecting the country code of the occurrence of traffic damage, when we use the intended functionality to register it, might be an example. As we know that the user will be able to indicate one of the codes defined in the database, we also know that when the app works he will not have the option to choose another code or a non-existent one. In that case, does it make sense to test how the application would react when the user chooses the country code “XXL”, if he will always only have options like, for example, “PL” and “DE”?
Another example of minimizing the number of tests while maintaining coverage is parametrization, a technique for consolidating scenarios. I encourage you to use it. Although we still create the same number of test cases using this approach, there are fewer test codes. Due to the multiplicity of conditions necessary for testing, this solution can be useful when testing classes of validators, calculators, etc. A disadvantage may be the lower legibility of descriptions of such tests contained in the names of the methods, which can usually occur during consolidation. However, this will not be an obstacle or problem if we decide to use modern tools in our project, such as the Spock Framework, which will make it easier to write assertions. These facilities will be obtained strictly from the Groovy language on which the framework is based. Groovy provides greater transparency in the context of tests and documentation. We will get the names of the scenarios easily, as well as having the option to add extra descriptions.
EXPERT KNOWLEDGE Agile Methodologies in Software DevelopmentFind out what agile means and what are different agile methodologies in software development. Go to article! |
This will also allow you to maintain a clear form of test cases, consistent with “given, when, then”. This will allow us to better document our functionality.
We can sometimes come across advocates of creating separate cases per scenario, that is, non-consolidation. But then we get back to the topic of future maintenance of all tests, as our application develops. There’s always “something for something…” However, it is usually best to maintain a balance. When you come across a complex aspect while searching for a golden mean, despite many similarities in test cases, in my opinion it’s worth writing them down separately, one by one. The categorical nature of the approach may lead to losing the usability that unit tests should bring to daily work. By focusing too much on the form, we may lose an understanding of the test scenario function.
Ensure the readability of unit tests
It is important not to lose the form while focusing on the function of testing analyzed areas. The proper arrangement and descriptions in the names of scenarios, methods or variables, documents given areas of functionality and allows us to use the test in a conscious and understandable way. I will risk stating that the measures of utility of a unit test will be both the reasonable coverage of the tested area and the legibility of its implementation. This approach will allow us to avoid a situation that many may have experienced, namely when after a long time it is difficult to start working on the development of a long-implemented area. Without carefully written, legible tests, our work will be an uphill struggle.
Benefits of TDD for the Client and the Developer
TDD for developers means:
- comfort of work – including with existing code thanks to fewer errors being made,
- documentation of functionality at a high level,
- better business knowledge and thus better know-how of the product,
- more chance of clean code,
- easier to start working on the project,
- reduced stress and pressure.
TDD for the customer means:
- more confidence that the application will work well and there will be fewer errors,
- an option to predict the costs of application maintenance and introduce modifications to existing solutions,
- savings on adding new programmers to the project,
- finding and correcting errors faster,
- greater chance of interesting suggestions from developers regarding the implemented solutions, thanks to better knowledge of the current functionalities,
- TDD means better code, and therefore a better application.
TDD development technique – summary
I hope that I’ve been able to show you what TDD brings to everyday work and what the consequences of not using it may be. The principles I mentioned play some part in minimizing the number of tests: parameterization, minimizing the number of them, omitting non-existent business cases in scenarios and the readability of the test code do not directly result from the definition of TDD. However, in practice it’s impossible to achieve real development by means of tests without reasonable observance. I believe that I have been able to present what the Test-Driven Development methodology is and how much value can be achieved by using it in custom software development.