Most modern languages that we use today provide tools and documentation for unit tests, which make it simpler to test our code. If unit testing is possible with any language, we can witness the simplicity to unit test languages like Rust, Elixir, or Ruby as compared to C, C++, or Assembly.
During my apprenticeship at 8th Light, all my code had unit tests. This has allowed me to increase my experience with unit tests and improve my knowledge surrounding them. I can say that I understand how they are beneficial, and where those benefits run into a limit. In this article, I want to quash three myths about unit tests that I once believed.
Myth 1: Tests == Quality
I used to believe that one of the benefits of unit tests was to improve the quality of the software. Although it is true that they encourage us to modularise our code and document it, unit tests do not check the quality of our code.
Unit tests cannot guarantee that our code is of high quality even with software that has 100% test coverage. Our tests do not check the consistency of our code. For example, they won't check if we have followed a guideline or left extra spaces.
In both of the above code samples the test will be green, indicating that both code samples are correct, but we will not have any feedback telling us which one is better.
If our tests do not check our adherence to the style of our code, it also cannot check the following:
- If your colleague can understand your code.
- If you have coded with intention.
- If you have used appropriate names.
Of course, these factors remain important for the quality of our software even if they cannot be checked by unit tests. Our tests and code reviews actively work together to ensure we produce software of high quality. Without these two important elements working together, we increase the risk of technical debt in our software.
2: Tested Software Is Bug-Free
Every programmer is different, but we all want software without incorrect or unexpected results. It doesn't matter if the effect of the bug slows down or crashes our software, they are never welcome. One way to check the correctness of our software is to write unit tests. The role of our unit tests will be to validate the expectations of the methods.
Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence.
Edsger W. Dijkstra, "The Humble Programmer" (1972)
This led me to believe that my unit tests were helping me to build bug-free software. But, these unit tests could be replaced by manual tests and they would catch the same amount of bugs.
What unit tests provide over manual testing is speed and thus, this saves us time. This speed will give you the opportunity to run your test whenever you modify your software, without wasting time and ensure to not create regression.
3: Tests Are Optional
We could believe that implementation and testing are two different tasks. After all, unit tests do not make a new feature work, no matter how beneficial they are. This made me feel that the unit tests were the cherry on the cake, not something integral to the taste.
If the implementation is needed to make the feature work, then its tests will be responsible for the correctness of the application, which is expected from a software. The unit tests have the same advantages and flaws as any code; they are fast-executing, repetitive tasks, but they can be difficult to maintain. We risk not putting as much effort into the tests and the implementation of a feature if we consider our unit tests as an optional extra. Underestimating the importance of quality tests over your production code will create a quality gap in both, where the cost of maintaining your tests will overtake its benefits. Quality tests should enable you to work faster, while also serving as good documentation for others to understand.
The test is part of feature, therefore should be considered upfront during the estimation of any new tasks.