Why Software Engineers Should (Be Encouraged To) Invest Time in Unit Testing

Milan Gatyás
Life at Apollo Division
6 min readFeb 11, 2021
Photo by Grant Durr on Unsplash

In this article, I would like to advocate a bit for why unit testing is valuable, not exclusively for the software engineers, but also for the whole delivery team, and ultimately for the company. Although unit testing is a mainstream technique these days, large groups of people and companies do not practice it. Naturally, there are use cases when unit testing makes little to no sense. However, especially in an enterprise software development environment, it almost always pays off. I have experienced projects both with and without unit testing practiced and will describe below what I learned through time.

Short Summary of Unit Testing Benefits

The biggest benefit and the primary goal of unit testing is the reduction of bug count that makes it into the production environment. Ideally, you will catch all the silliest types of bugs, such as missing or wrong variable assignments. Additionally, while writing the unit tests, you think about your produced business code in a different way. Instead of focusing on the code design, you focus on the code functionality against placed requirements and acceptance criteria. This helps to think about the edge cases of your application behavior, such as different casing of the strings involved in comparison, absence of an expected value, or error handling of exceptions.

If you want to have a blast while unit testing, you should write the code to be unit testable in the first place. Usually, writing the unit-testable code promotes the single responsibility principle, code isolation, and loose coupling of your business code. As you want to write the unit tests as small and simple as possible, you also inherently shape your business code to be as small and simple as possible. Having a hard time writing unit tests usually signals to you that the tested entity is too complex, or not isolated enough.

Unit tested codes bring confidence to the development team. Your team (usually containing newcomers) might be afraid to improve the quality of the existing code due to the risk of changing the existing behavior of the application. When business requirements are captured in unit tests, the team is confident to perform a refactoring of the business code, as the unit tests are usually untouched. When they need to adjust as well, the risk of changing your application behavior is present, but usually low as updating the unit tests means mostly the reshuffling of the code.

When written in an understandable manner, unit tests can serve as a good source of documentation of your business code. You can also use Gherkin tools such as Cucumber for Java, SpecFlow for .NET, etc., to represent your unit tests as Gherkins. When an agreement is made between business analysts and developers on the common keyword set, business analysts can “write” the unit tests directly in Gherkins with the support from developers ensuring bindings to the underlying unit testing framework.

Most Used Arguments of Naysayers

Let us look at the most commons arguments of unit testing “deniers.” While some are valid, there is usually also the other side of the coin.

Unit testing is worthless
I hope the text above is making at least a slight point that unit testing is not worthless.

Unit testing is expensive
Writing unit tests brings additional effort and time to the development team. You can easily write as much unit testing code as you wrote business code. From my experience, unit testing reduces the number of bugs made during development. Well, bug fixing and hot fixing cost money as well. Furthermore, reputational damage is something that should be considered. It is much better for the prestige of the company to spot a bug in the development phase rather than in the production environment.

Unit testing is taking a long time
The development time of the feature can be doubled when focusing on well-made unit tests. It should be taken into consideration when planning the work. As mentioned above, bug fixing, and hot fixing takes time as well as money. The option to go when being out of time is to write the business code with technical debt, without unit test coverage. Unit tests shall be supplied as soon as possible, ideally before the production release to reduce hot fixing risk.

How Unit Testing Benefits Everyone

Unit testing can be an undeniably helpful tool for software engineers. The great part is that other people in the company are benefiting from it as well. It is ordinary that while unit testing the edge cases of the required application behavior, new questions arise, affecting the business requirements and acceptance criteria themselves: What if gift code is applied while the account is in a free trial? What if a blocked user is trying to update his payment method? What if the geo-located country of the device is different from the user’s sign-up country while requesting geo-locked content? Surely, other team members will be (and should be) thinking about edge case situations as well. However, you as a developer have a different point of view since you have direct access to the source code and can think about those cases in a unique manner. By discovering these exotic cases and discussing them with the business analysts, business requirements are “stress-tested.” They can be improved to cover the found use cases and potentially interpolated to other similar cases. Sometimes, these discussions can expose a gap in the requirements which brings back the discussion about requirements to the product owner. Having explored and discussed the relevant edge cases, quality assurance engineers get familiar with them too (via acceptance criteria) and include those scenarios into their QA processes. This can bring another set of new questions and findings regarding the behavior of the application. Ultimately, the managers and the company themselves are benefiting from this process as the team is pro-actively questioning the product owner’s provided requirements, it is producing a code that is stable, reliable, and bug-free.

Essential Unit Testing Tips

Let me wrap the article up by sharing the key points that I find most important in order to be successful with unit testing in a simple example. Below is the code snippet (it is in C# language, yet reasoning applies to any programming language).

The class is tightly coupled with a specific database instance, and, usually, you will be lucky to be able to mock the instance behavior. It also depends on the DateTime.UtcNow property which you cannot control for the sake of your unit tests. Furthermore, it is responsible for performing both input validation and database operations. All of this contributes to low unit testability.

Abstract away the dependencies on concrete implementations. Hide database connection operations behind the interface so that you can easily mock it. Do the same with the date-time resolution.

Split the responsibilities. A specific entity can focus solely on the validation of the input (if complex enough). You can then unit test validation logic in an isolated way and mock it in the Processor class.

Focus on the edge cases. What if certain data of input is not set, or set in an exotic, unexpected way? What if the data persist operation fails? Together with testing of the “rainbow” scenario, edge case unit tests are usually the most valuable tests you can write.

PS: If you want to read more about the practices that help you to write well unit-testable code, check out the article series about the software development best practices.

We are ACTUM Digital and this piece was written by Milan Gatyas, .NET Tech Lead of Apollo Division. Feel free to get in touch.

--

--