There is a very common problems among programmers that haven’t tried unit tests .. They don’t know how to start. Most of the programmers that I’ve met, including myself, had this problem, and the natural way to go about this is to buy a book and start reading.
Problem is, I had some really bad experience with books on that topic, so did the people I met.
Today, I have finally had enough unit test experience that allows me to explain how to go about this.
Let’s be clear first … There are many types of testing, unit testing, system testing, integration testing, black-box testing, white-box testing, … etc. Make sure you read about all types of testing methodologies, because they will teach you what unit tests aren’t.
To highlight some of the important topics here:
Unit testing isn’t BDD
BDD is writing high level tests that the business understands and can be expressed in terms of the system output. That means you don’t really care about the internal workings of the system as long as the correct output comes out.
Integration tests, functional tests, system testing aren’t much different. They only differ in subtle ways, but they are all high level testing that usually doesn’t need knowledge of the internal system workings.
What Unit Tests are
Unit tests are tests that are written for every single part of your system. You test that your object contains certain properties, you test that invoking a certain method returns a certain result, and you even test the output signals generating from your system. It is a very low level rigorous testing.
Now, here are the enlightenments I had while writing those tests…
The fact that you have to test every single operation in your system makes you never want to write duplicate code .. ever. If you write duplicate code, you will duplicate your tests, and things will start looking shitty. I have a rule that, once you start copy pasting your unit tests, that means your program isn’t DRY enough, or you’re doing it all wrong.
One reason you might end up writing duplicate tests is not necessarily duplicate code. You might be mixing BDD with TDD. Here is an example:
Notice that the component receives an operation that changes the behavior of this
Component class. How are you suppose to test this?
If you are new to TDD, you might be tempted to instantiate a
Component, set the operation to something, and then make sure that operation performs properly. That is not proper TDD, because you will be duplicating the testing of the operation, which is external to this class!
The test for this case is to simply pass a dummy operation with a mock object, and assert that when we call the
perform method, the mock object will be called:
I hope the above example is clear. We are simply testing that if we pass an
operation to the
Component will perform a call using that
perform is called.
Fortunately, you are a passionate programmer, you can’t leave things the way they are! So, while writing unit tests for the system, that will be a perfect opportunity to find and eliminate duplication, and discover better ways to come up with abstractions.
Imagine if for you to write a test for a function that is the handler of a button click. So, on button clicked, you want to send an HTTP request, and schedule a notification (those are totally random things, but you might be doing something similar). Testing this function because almost impossible.
You have to instantiate the HttpClient singleton, initialize the notification scheduler somehow in order to for the function to be tested properly .. That won’t do at all.
The rule is:
When writing unit tests, you must test each component in isolation.
Taking the previous example into consideration, we first need to make this function serve one purpose, and then test that alone. So, what we might have is another core component, completely separated from the UI, that receives a signal through some
This way, all we have to do to test this function is make sure that it submits that notification! As for the other stuff that needs to happen, we can design it in the observing component.
In this case, the component might have services registered to handle certain signals. So, there is an HTTPService that registers for that signal, and will send a request when that signal is received. Notice, that now we can test this HTTPService separately! The test is to make sure that when a signal is generated, the HTTPService attempts to send an HTTP request.
Catch Bug Early
Needless to say, that one of the toughest things we programmers go through is finding the origin of a bug, especially a semantic one. By unit testing, you can verify all your assumptions you are making by simply running the tests. That will help you catch bugs faster, because you are testing your changes at a micro level, before running them at a macro level.
This is especially useful when you are writing C/C++ code, or in some scripting language, since it’s easy to make mistakes there. Something could easily slip by because of the extreme low-level/high-level programming.
Yeah, I am not gonna go through other obvious TDD benefits that people evangelize in books, since this post is all about practical advice that I actually experienced.