How to Test your Production Code: Everything You Need To Know About Test Doubles

One common objection that I get while explaining TDD to my colleagues is that they deal with objects that are too complex to be tested in an automated suite. They either have methods that are too complex (too many interactions with other libraries or classes) or objects too difficult to instantiate (constructors that call to databases or that need to set up a huge graph of dependent objects).

Sometimes the objection is that the program uses some method that reaches to a database or an external service to read (or worse to persist) some information. This normally happens when the developer is working with some legacy code that doesn’t have any real separation between what is the business logic of the software product and what are the external resources that it uses (databases, web services, etc).

Of course, writing automated tests to verify code that calls to a database to save some information can be painful for several reasons:

  • Databases are slow: the test would run slow and you will not be willing to run them often;
  • After every test you have to clean the database: this is important because tests should be atomic a reproducible, if the database contains old data, the tests could produce false results. Of course this result in even slower tests;
  • Your software may be using a database (or service) shared between different applications or different developers: if you are working with a shared database your tests may not  be reproducible, you cannot clean the records because you could be deleting other people data. Sharing a database between developers makes your code untestable without some sort of separation from it;

To be able to test your code you have to be isolated from the parts that are difficult to test. By using other objects with the same interface as the one that we want to avoid using.

We have all seen videos of the crash test done on cars to certify their safety. Obviously it wouldn’t be acceptable to do the crash tests with real humans (luckily the time when living humans were used to test katanas is long gone) so they use a dummy that is loaded with sensors to measure the effects that the crash would have on real people.

The software counterpart of these dummies are called Test Doubles and are used in the same fashion: during the tests they are put in those places where it wouldn’t be acceptable or easy to use real production objects. We use Test Doubles to put the software in the condition that we want to test.

Let’s try to understand this better with an example:

public bool Contains(string searchString)
{
    try
    {
        // networkHandler is an instance of an object
        // that makes request to a uri
        List<string> fromNetwork = networkHandler.GetAsList(uri);
        // some interesting code
        return true;
    }
    catch(WebException e)
    {
        return false;
    }
}

The method Contains uses an instance of the class NetworkHandler to load a list of strings from somewhere; this class is defined like this:

public class NetworkHandler
{
     public virtual List<string> GetAsList(string uri)
     {
          // some code that connects to the uri passed as parameter
          // and then parses the result and returns a List of strings
     }
}

Using the real NetworkHandler there’s no easy way to test that our method returns false when a WebException gets thrown by the method GetAsList. The best way to test this behaviour is to replace in our tests the instance of NetworkHandler with a test double which only purpose is to throw a WebException when the method GetAsList gets called.

public class NetworkHandlerStubThrowNetWorkException : NetworkHandler
{
     public override List<string> GetAsList(string uri)
     {
          throw new NetworkException();
     }
}

Types of Test Doubles

The Test Doubles are divided into different categories depending on their usage in the testing suite. Returning to our dummy analogy, a mannequin is a different Double than a fully featured Crash Dummy, both have the resemblance of a human but they are used in totally different scenarios. In the software world we have:

Each of which has it own purpose in helping us isolating the code that we want to test from the one that not; In later posts we will explain each type more in-depth, giving  also some usage examples.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s