BlogTechnology and KnowHow

Unit Tests - Whose responsibility is it anyway?

Symphony logo
Symphony
July 27th, 2022

QA Engineers write and perform tests. Unit tests, as their name implies, are tests. Shouldn’t QAs write unit tests as well, then? The straight answer to this question should be - absolutely not. Writing of unit tests should be reserved solely for developers. However, the more interesting question is why QAs shouldn’t write unit tests, and that is the topic we will be exploring in this blog post.

Preface

One of the common counter arguments is that QAs might not be familiar with the codebase, programming language, or framework of choice to write unit tests efficiently. While this might be true in some cases, it also allows for an easy “way out” of this discussion. Thus, for the purpose of this article, we will be assuming that a QA is perfectly capable of writing efficient unit tests and why they should choose not to do so regardless.

In addition to this, we will assume that we are not working in a TDD environment. Even though it is an excellent approach to software development, it is also relatively rare in practice. If you are working in a TDD environment, this post will most likely be redundant to you. However, if you are a QA who has been asked to write unit tests in the past, this blog post should be right up your alley.

Business value

QAs are, by default, focused on the bigger picture rather than the details of the implementation. Due to this, the business value is going to be of most interest to a QA, seconded only by the efficiency of tests.

Unit tests are as close to the implementation details as they get. This makes it very hard for a QA to “zoom out” and actually treat the application under test as a black box. It is of utmost importance that this approach is taken at least in the beginning because it most closely mimics a user’s perspective. In addition to this, they are testing very narrow and isolated pieces of code, which, by themselves, rarely carry critical business value. Usually, it is only when several units start collaborating that the business value starts to grow. However, this collaboration is covered with integration tests to preserve that business value.

It is this combination of low business value combined with a tight tie to the implementation that makes unit tests less desirable to QAs. A more effective approach would be leaning onto integration tests to capture higher business value, as well as allowing the QA to treat the application as a black box from the API level down. All of this usually comes at the cost of slightly lowered execution speed, but the value that we get back absolutely justifies the cost.

It is worth mentioning an exception to the statement above - when a unit carries immense business value. An example of this could be a method within a financial library that calculates an effective interest rate for a set of input parameters. Having access to the method would allow the one writing the test to generate numerous test cases with different input parameters that would be executed as efficiently as possible. Additionally, this functionality might be slower to access from either UI or API layer. Thus, it is worthwhile to consider offloading most of the test cases to unit tests.

In this case, it is recommended to combine the QA’s overview of the bigger picture with the developer’s expertise and have the QA contribute to the unit tests design in quality assistance capacity. A QA would be sharing their insight and advice on what test cases to design, while the developer would then easily implement unit tests. This approach would additionally allow the QA to know exactly which test cases they might not need to cover on the integration or E2E level, saving time and effort.

Slower process

We should constantly look for ways to make our development process more efficient. One of the optimizations we should aim for is “shifting left,” i.e., having QAs test and design automated tests as early in the development process as possible. In practice, this requires the participation of the entire team in user story refinements in order to detect what needs to be built. As the second step, the team would be required to also participate in the technical specifications of the feature to understand how it will be built.

Once the technical specification has been completed and the contract between Frontend, Backend, and QA Engineers has been set in stone, each role can proceed with their side of the implementation since their integration points are now known and documented well. For a QA, this would mean creating the UI and API tests even before the latest code has been made available in an environment. This is possible because the contract that we are creating the tests against is relatively straightforward. For example, all that we require for API test creation are the details of the future endpoint (verb, URL, body, etc). In other words, we just need the interface to design tests against, while the implementation details should ideally be treated as a black box anyway.

This is almost impossible when it comes to unit tests because they are tied to the implementation as close as it gets. Moreover, in an ideal scenario, unit tests should dictate the design of the tested code, or in a less ideal scenario, the code will dictate the design of unit tests. One should help the other evolve. If we were to separate these two mutually beneficial efforts and delegate them to two different people, this would result in unnecessary communication bloat. To achieve the same result, we would have two people report even the slightest change to each other if we are aiming to achieve the benefits that unit tests bring to the code design. If we were to eliminate that frequent communication, it would probably mean that we are sacrificing one of the most important benefits of unit tests. This brings us to our next topic…

Code quality

As mentioned before, unit tests are tightly tied to the code itself. This puts special emphasis on the actual testability of the code. If you find tests hard to write, you are most likely not adhering to interface segregation and dependency inversion principles from the SOLID design principles.

Therefore, if a developer cannot easily write unit tests for the piece of code they are currently working on, that should be an excellent signal that the mentioned design principles were not respected and that the current implementation should be revisited. Following this, if the developer is not familiar with the design principles in question, this would create an opportunity to learn and understand how to introduce these principles in their work.

Learning these principles would lead to code refactoring. Once the code has been adjusted accordingly, unit tests should be relatively easy to write. If the developer uses this knowledge consistently in the future, it will lead to them honing their unit testing skill and effectively wielding this knowledge. At the end of this journey, unit testing can and will influence how they design their code forever - in a maintainable, loosely coupled, and therefore, testable manner.

Let’s observe the alternative path of this journey. Let us assume that the QA was supposed to write unit tests for the codebase that we initially described above without actually changing the codebase itself. In this case, the QA would simply be stuck.

In the best-case scenario, they could write subpar unit tests that rely on hard dependencies instead of mocking them. In the worst-case scenario, they would not be able to design a unit test for a particular piece of code at all.

But the worst outcome would be if they were robbing the developer of an immense opportunity to grow as an engineer and a professional.

Conclusion

So whose responsibility is it to write unit tests anyway? Instead of looking at it that way, we could instead take a look at who gains the most value from writing unit tests and draw our conclusion from that.

On the one side, we can see how many potential benefits there are for a developer from learning and mastering unit testing. On the other side, a QA attains very little to no benefit from writing unit tests specifically. To top it off, even if it were to bring any value, it also comes at the cost of a significant organizational overhead.

Thus, we should reserve the writing of unit tests for our developers. And then, we should support their implementation with our perspective and insight whenever possible. After all, the best results come from strong collaboration within a team!

About the author

Slobodan Popovic is a Principal QA Engineer working at our engineering hub in Belgrade.

Slobodan has been working as a QA Engineer in Symphony for over two years now, but his career beginnings date back to 2014, when he first encountered the world of QA. He was introduced to QA testing during his Law studies, and he was instantly hooked. With very little experience but an enormous drive to learn and evolve in the desired field, he applied for his first job and started his QA journey.