Preamble#
This article is not intended to cover the whole testing process due to its vastness. Having a glance at its Wikipedia page could give an idea of it. I am going to cover only the tip of the iceberg, but it is important to remember that bugs cannot be entirely eliminated from software — instead, they must be discovered as soon as possible.
The higher the time passed since the bug came into the system, the higher the cost to fix it.
So any techniques that could be useful to identify a bug should be used, taking into account the resources available and the type of system to be implemented.
I am going to focus on the developer’s point of view: what can a developer do (related to software testing) to improve the quality of the code? The first word that comes to mind is TDD. Of course, as already said, this isn’t the only way to implement software testing, and it is far from me saying that it is enough, but surely it is a good starting point. I strongly believe that TDD/BDD are very supportive techniques that could help writing more robust and stable software. The problem is mine: I haven’t ever applied it from scratch so far, so I decided to address this gap from now on.
Although I have been programming for about 5 years, I think I have very little practical experience with software testing. Please don’t misjudge me for that. In the past I wrote some tests, but usually while working in an already set-up environment, writing small tests for the functionality I was adding to the system. Here I am talking about establishing a test-oriented development process.
I won’t consider any language-specific framework, but I am going to define only some of the main keywords. I am going to dive deep into the details of a specific framework (hurrah, practice!) in a dedicated article, but not here.
Keywords#
Unit test#
Unit test refers to a single unit of code. What actually is a unit? According to the paradigm used, a unit of code can be a function or a class in the case of object-oriented programming. The important thing is that unit testing should not be performed by considering the success case only; on the contrary, the unit test should take into account, in particular, the corner cases, in a defensive programming manner. Why? Because, as a developer, you will run through the normal workflow dozens, hundreds, or maybe thousands of times.
Integration test#
On the other hand, integration tests should refer to the interaction of multiple units of code, by trying to locate interface issues. The integration tests can be seen in a hierarchical way: once a group of units are tested, it can be considered at the same time as a unit, a single module, to be integrated with other modules, so an integration test can be performed in an upper layer, and so on. Anyway, the importance of integration testing should not be overlooked, since with unit testing alone it is not possible to identify defects at the interface level.
Mock#
Mocking during testing is fundamental. It is the process of creating an object that can simulate the behavior of a real object. This is necessary because, in the real world, a unit is often composed of or depends on other units. To make sure the code under test is isolated, and to avoid side effects that can occur in other units, mock objects should be created — objects that simulate what the real object would do, but without the risk of defects occurring externally to the unit being tested.
TDD#
Test-Driven Development is a software development process which involves the creation of the test before actually implementing the functionality. The process involves the following steps:
- Verify whether the current design fits well with the functionality to be added.
- If necessary, refactor the code to adhere to the new feature.
- Repeat the tests, to be sure that after refactoring everything still works properly.
- Write the tests for the new feature (at this point, all the new tests should fail).
- Implement the functionality to adhere to the specification represented by the tests (at the end of this step, all the tests should pass).
- If necessary, refactor the code just implemented (clean up if necessary, remove duplications, etc.).
- Run the tests.
The upside of the process is highlighted by steps 1 and 4: step 1 makes it possible to refactor the existing code without being afraid of it and without losing confidence; step 4 is useful for keeping the code clean, improving readability and maintainability; it can also, in some cases, be useful for trying to figure out whether there is a solution that improves performance, and such an operation could be performed in step 4.
BDD#
Behavior-Driven Development can be seen as a specialization of the aforementioned TDD, but it is oriented to the user. It involves the definition of the tests as a description of the desired behavior. It permits a clearer way of communication among different actors, when the process involves not only developers, but also QA Testers, who can define the specification of the tests in a domain-specific language, which developers can then translate (also by means of automated tools) into a specific programming language.
Conclusion#
If I’m not the only person in the world who hasn’t yet established a real TDD strategy in their daily job, I strongly recommend giving it a try. Keep in mind that a very robust system should be tested as much as possible, from every perspective, both functional and non-functional, taking into account also features too often overlooked, like security and performance; so please also consider investigating performance testing and security testing — I will try to describe them in a related post.
