Skip to content

Testing

I have been thinking about how to approach this post for almost a week, and in the end, I decided that I am going to do it with honesty. The following are my opinions, and I take full ownership of everything I am going to write. So, fair warning, long rant incoming.

I am sick and tired of hearing bullshit related to unit testing. I have a really hard time figuring out how any developer can be fine with delivering untested code, code that nobody knows if it really works until it is tested in production, with real users. I am sick and tired of hearing always the same arguments, the same excuses, justifying not writing unit tests. And I hate how the idea that unit testing is not indispensable has made its way into some management circles.

These are some of the excuses I have heard to not write unit tests.

We don’t have time to write unit tests.

Well, when you say that, what I hear is “I don’t know how to write unit tests”.

Why? Because of this:

Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation.

That’s the first result when googling “unit tests”. And I think it is a very good definition.

A unit test should test a small unit of behaviour. If you expect a class to return a specific value from an specific method when some particular conditions are met, all you have to do is test that you get the value you expect from the method you are interested in after recreating your particular preconditions. That is not that hard. That is not that time consuming.

And if it is, if testing the smallest unit of behaviour in your system is hard and time consuming, then your smallest unit of behaviour is not as small as you think. Back to my argument: you would be testing it wrong.

If I change the production code I will have to change my tests.

Well, obviously, if you know that the behaviour and the public API of a module are going to change tomorrow, first thing in the morning, it might not make too much sense to unit test that module today.

But… how many times will you find yourself in that situation? Not many.

Of course, a codebase is similar to a living and breathing organism, that evolves and might change its behaviour over time. Well, since you will have to refactor your production code, your will also have to refactor your testing code to reflect the new behaviour of the system.

But justifying not writing tests because you will refactor your code in the future does not make any sense. Will you also refuse writing code, today, because you might need to refactor that code in the future? I don’t think so.

Testing is difficult and requires too much work.

Well, sorry, but if testing your production code is very difficult, your production code is not properly designed.

You might be trying to test classes with too many responsibilities (violating the Single Responsibility Principle) you might be testing private behaviour, or you might be testing multiple layers at once (therefore, trying to write something that is not actually a unit test). Either way, when testing gets hard, that’s a clear sign that the design of the production code is wrong.

Or, to put it another way, well factored code, with small, highly cohesive and loosely coupled units, is easy to unit test.

Testing private behaviour is very hard.

This point is not too different from the previous one.

If a behaviour is private, it should not be tested. If you really really want to test it, it means it is not private. So, we are back to swore one: the code is badly designed.

Now, allow me to digress a little bit, and tell you a story. A couple years ago, my team spent an insane amount of time trying to test a class that was performing some network requests, and the cached the behaviour. Cache was supposed to expire after five minutes, if I recall correctly. While caches were valid, the class performing the networking would return cached values, when caches were invalid, it would trigger a new network request. The production code looked like this:

The team complained a lot about how difficult it was to test that behaviour, how difficult it was to mock the cache, and spent an insane amount of time researching mocking frameworks.

All we needed was turn to the Dependency Inversion Principle. Inject the dependency like this:

And now the behaviour of the cache could be tested in isolation. And the behaviour of the class doing the networking could be tested in isolation. And the collaboration between the both of them could be tested in isolation as well.

Testing the UI is impossible. How are you going to unit test a view controller?

What do you mean by “unit testing a view controller”? Or “unit testing an activity”? Because what I have found people usually mean by that is something along the lines of “how am I going to test that when I tap a button the text in a textfield is updated with new content dowloaded from a server”?

The UI layer should be thin. Very thin. It should only render data, and propagate down user interaction. Period. If it does something else, the design is wrong. Period.

Some people call it The Humble Object Pattern, but I don’t think we really need a name for it. The UI layer should not do any “business” logic. If the UI layer needs to coordinate state between UI elements, that logic should be extracted to different units, because of modularity, and encapsulation, and good separation of concerns, but also because as an added benefit, it will be easier to test.

By now, I am sure you see the pattern: well designed code is easy to test. But the opposite is even more accurate: poorly designed code is difficult to test. And the more difficult it is to test a particular unit of code, the poorer its design.

I can’t waste time! I need to ship!

Do you want to ship code that works? Or do you want to just toss a dice?

Did this post offended you? If that’s the case, to be honest, I don’t care.

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *