Loading...

A "Unit" In A Test Is Not The Class Under Test

December 5, 2023
2 minutes to read
Share this post:

Hi,

What is actually a “Unit” in a Unit Test?

Dumb question, you might say.

But it’s an interesting one. For many years, like most people who talk about this topic, I believed:

A unit is a class that I want to test.

And maybe you saw it that way too. And you certainly had a few problems with it.

For example, the symptoms of resulting Coupling

What happens when we write a UserServiceTest class? We have - without realizing it - established a semantic coupling between the UserService class and the UserServiceTest.

If we rename the UserService, then we also have to rename the UserServiceTest. How often I have forgotten that.

Just three years ago, I wrote the blog “Best Practices For Unit Tests” .

In it, I recommended writing the method signature in Java in the form methodName_conditions_expectedResult.

Same problem. A semantic coupling between the test and my implementation. Renaming the method leads to renaming the test function. Or - more likely - to confusion because I forget it.

In the same blog, I described another pattern that I now consider an anti-pattern.

I recommend (almost) mocking everything .

But, if we mock everything, then we only reflect the implementation - in slightly different words.

In the first sentence of this paragraph, I express what most developers live today:

A unit test - by definition - tests only, and exclusively, the unit under test.

But this is exactly where the problem lies. The definition is wrong.

In the original definition, “Unit” meant the independence of the test itself.

It was a widespread problem that tests were built on each other. This term was meant to clarify that a unit test is independently runnable.

All the problems I have described cause a strong coupling between the implementation details and the test. But we don’t want to test the implementation.

We want to test a behavior.

And that’s what you need to focus on. Instead of isEmpty_emptyList_true, you want a listWithNoElementsIsEmpty test.

Do you notice how the mindset changes? I described the behavior of a list. It’s no longer about testing the API.

When we detach from the implementation, everything becomes easier.

Tests tell us which behavior no longer works. Refactoring no longer automatically leads to many false positives. We write fewer tests. But cover the same.

Try this out for your next feature 🙂

Rule the Backend,

~ Marcus

Top