This blog is a translation of my article that’s published by the Dutch Microsoft .NET Magazine (http://www.dotnetmag.nl/Artikel/2683/Unit-testen,-hemel-of-hel)
Unit Testing is a practice of which we are all convinced that it’s a good thing. I’ve seen many teams that start out very enthusiastic with Unit Testing, but in practice it’s often disappointing.
Tests are time consuming to maintain, slow running and sometimes it’s just hard to get some code under test. For these and other reasons, Unit Testing is a practice, that when under pressure slowly fades into the background. Even when we’re on paper convinced that Unit Testing offers advantages.
In this blog I’ll discuss some errors that often cause problems and some best practices how we can tackle these.
What exactly is a Unit Test?
Many of the problems that teams encounter when using Unit Testing, arise in the definition of what exactly is a Unit Test. A Unit Test is all about testing the smallest possible piece of code to see if it meets the requirements. A Unit Test that uses a WCF service and talks to a real database is not a Unit Test.
Good Unit Tests thus provide the basis for a high quality, stable product.
But what’s so difficult about writing a Unit Test?
Writing a Unit Test is really not that difficult. Writing good testable code, however, is much more difficult and that’s where most of our problems come from.
But how can you make sure that your application can be tested as lot of small, individual parts and why does this often go wrong?
The development of an alarm clock radio
As an example, in this blog I’ll look at developing the software for a simple alarm clock that uses a service that retrieves the current time from a satellite. The hardware vendor has provided us with the following wrapper code:
A first attempt for creating our Alarm Clock looks like the following:
It’s now easy to create an Alarm Clock and use it. But how would you test this code?
If we look at the definition of a Unit Test, we see that it’s about testing the smallest possible piece of code. If we now test our alarm clock, then we’re also testing the Hardware Client and the Satellite Sync Service. Is that test the smallest possible piece of code?
Mocking and Dependency Injection
There are two important changes. Instead of working with concrete classes, we work with an interface. In addition, the class now clearly states what he needs and gives us a logical seam where we can control this.
The task of a class is to implement business logic, it’s not his job to create all kinds of objects. Look therefore whether the use of the new operator (or static data or singletons) in a class really belongs there together with business logic. Or should you move the construction logic outside the class? Make sure that you have seams in your code so you can replace a dependency in your a unit test.
Using Rhino Mocks we can create a new clock radio in the following manner and test it:
Of course you have to choose for each specific situation if you want to pass the dependency in the constructor or only to a specific method.
The Law of Demeter
Suppose you’re at the checkout and the cashier asks you for twenty euros. Would you give your wallet to the cashier so they can get the money out or do you take the money out of your wallet and give only the twenty euro?
This principle is called the Law of Demeter. Your code should only talk to his immediate friends, not against foreigners. So only pass the data that a function really needs. This in turn ensures better testable code with fewer links to other parts.
Generating test data
Another problem with writing unit tests is the test data that we need. Often this test data can be complicated to construct, which in turn makes a test very long and difficult to read.
Consider the following initialization code to test whether the price of an invoice is written to the database.
The only detail which is mentioned here is the price. All other required data is automatically filled with dummy data. Besides readability, this has another advantage. If there is any changes to the Invoic, Customer, Product, or Address class that has nothing to do with the price, we don’t have to make maintenance changes to this unit test. The Builder Pattern can save us a lot of time as a project grows.
Unit Testing is a practice with a lot of potential. As developers, however, we must remain committed to writing better testable code. Then Unit Testing will pay of and we will have the advantage that better testable code leads to better code and fewer bugs.