The Unit in "Unit Tests"

In the last month, I heard a lot about the "'unit' in 'unit test'", as in "the unit being tested". If you do that, stop.

The thing about "unit tests" is not that they are "tests for a system unit" but that "the test must be a unit" -- in other words, the test must be a single thing that doesn't affect anything else and isn't affected by anything else.

The fun part about confusing "unit being tested" with "a test must be a unit" that some people came with the idea of BDD, which, in essence, is a TDD. Most people think that BDD means "I have these requirements and that's what I'm going to test, instead of writing a test for every single function I have". This is, again, wrong.

For example, let's say you have a requirement that the output of a user profile should be their title, first name capitalized and last name, also capitalized. You do the right thing and write a single function that concatenates the title, the first name and the last name; the next time is writing a test for it, so you write a "unit test" that does a bunch of conversions and make sure the function works flawlessly. Then you give yourself a pat in the back and move on. This is -- and you'll get tired of hearing me saying this -- wrong.

"Why testing a function is wrong? Shouldn't a function be the smallest unit to be tested?" Well, maybe, but you shouldn't focus on that. You see, the requirement for the way of outputting a user profile is the thing you should test. Does it display the title, first name and last name in that order, with proper capitalization? Then you have everything right. Now, let's say that the requirement for outputting the user profile changes; you simply change the output function or, depending on the request, simply stop calling your magic function. You change the test that checks the output (the behaviour of outputting the user profile) and happily you go. Did you noticed that the function that you created is never used again? Do you realize that coverage tests will not point that that function is not being used anymore? Do you realize you now have a dead code kept alive only because there is a test that is not useful anymore?

That's the weird fact about trying to find a "unit" in "unit tests": It already exists in the form of requirements. Writing single function tests is not useful because it will never point out dead code because your tests -- a test that it is not required anymore -- will keep your code alive. If you make sure your behaviour is following your requirements, whatever is going on on the background is unimportant; basically, you have a "black box" testing, in which you don't care how things work, as long as they answer it properly.

So, what should you test? First and foremost, your requirements. We talk a lot about E2E -- End-to-End -- Testing. This is a good test because you usually test the requirement in the process. I'm not going to lie to you: it's damn hard to do so, because you usually have to mock everything outside your project simply to test something instead of simply creating a new object and calling a method. But that also means that you can change whatever the way your objects, functions, methods, modules, database and, in some extreme cases, programming language are inside and you still have the same response.

Think like this: Instead of simply writing code that outputs something, you'll build an API: User gets in, title, first name and last name gets out; you write a good interface for the user to get in and a good interface for the full name to get out, never worrying about the way things work in the background. Write code in isolate pieces that can be connected and write tests about those connections, not their internals.

Everything is an API -- including that little piece that gets and user and outputs their fullname.

Go Top