How to Test your Production Code: Using Test Stubs

In our last post we explored how to use Dummy Objects in order to test our code when we are dealing with objects that are difficult to create. One important restriction on the usage of this kind of test double is that we have to avoid touching the dummy object during our tests. We can easily check that LoadInvoicesByDate throws when from is higher that threshold:

public string WarningMessage;
public IList<Invoice> Invoices;
public void LoadInvoicesByDate(DateTime from, DateTime to, DateTime threshold, IDbFactory dbFactory)
{
if (from > threshold)
throw new DateOverThresholdException("from");
var count = dbFactory.CountInvoices(from, to);
if (count == 0)
{
WarningMessage = "No Invoices in given rage";
return;
}
Invoices = dbFactory.GetInvoices(from, to);
}

But what happens if we have to pass the first ‘if’ and check that the text of WarningMessage is updated when count equals zero?

In this case the variable count is said to be an indirect input of the code we want to test and in order to control it’s value we need to instantiate a special version of dbFactory that we have under our control. Of course we don’t want to use the real instance of DbFactory because who knows the state of the database… we could never get a count that equals 0. So in our test we are going to need a Test Double called, you guessed it, Test Stub.

The Test Stub will provide you with the indirect input you need no matter what how it gets called. Let’s explain with a test that checks  that the WarningMessage field is set when dbFactory.CountInvoices returns zero:

private class DbFactoryStub : IDbFactory
{
public int CountInvoices(DateTime from, DateTime to)
{
return 0;
}
// other IDbFactory methods
}
[Test]
public void WarningMessageWhenCountIsZero()
{
IDbFactoy dbFactory = new DbFactoryStub();
invoicesManager.LoadInvoicesByDate(
DateTime.Now, DateTime.Now, DateTime.Now,
dbFactory);
Assert.AreEqual("No Invoices in given rage",
invoicesManager.WarningMessage);
}

What’s happening here?

First I create a new class that implements IDbFactory and set the return value of CountInvoices to zero, which is exactly the return value we want to have. With this test double we are forcing our code through a path that could have been difficult to simulate with the real implementation.

Moreover, we can generalize a bit our dummy class and set the wanted return values in the tests

private class DbFactoryDummy : IDbFactory
{
public int DummyInvoicesCount;
public IList<Invoice> DummyInvoices;
public int CountInvoices(DateTime from, DateTime to)
{
return DummyInvoicesCount;
}
public IList<Invoice> GetInvoices(DateTime from, DateTime to)
{
return DummyInvoices;
}
}
[Test]
public void WarningMessageWhenCountIsZero()
{
IDbFactoy dbFactory = new DbFactoryDummy();
dbFactory.DummyInvoicesCount = 0;
invoicesManager.LoadInvoicesByDate(
DateTime.Now, DateTime.Now, DateTime.Now,
dbFactory);
Assert.AreEqual("No Invoices in given rage",
invoicesManager.WarningMessage);
}
[Test]
public void InvoicesSetGetInvoicesReturnedValue()
{
IDbFactoy dbFactory = new DbFactoryStub();
dbFactory.DummyInvoicesCount = 1;
dbFactory.DummyInvoices = new List<Invoice> { new Invoice() };
invoicesManager.LoadInvoicesByDate(
DateTime.Now, DateTime.Now, DateTime.Now,
dbFactory);
Assert.AreEqual(string.Empty,
invoicesManager.WarningMessage);
Assert.AreEqual(dbFactory.DummyInvoices,
invoicesManager.Invoices);
}

With this change to DbFactoryDummy we are able to configure its return values while testing in order to use the same dummy class for different test cases.

Remember that our goal isn’t that of testing the behaviour of the particular implementation of DbFactory but how our code behaves when stimulated with different indirect inputs.

What if we want to test the indirect outputs of the system? for that we use Mock Objects, that will be explained in the next post.

Author: Daniele Pozzobon

Daniele is an aspiring software craftsman with more that ten years of experience in the software industry. He is currently a consultant in the .Net space for a big insurance company, and previously have worked as a web dev in the manufacturing industry. He has experience with C#, Java, C++, PHP, Javascript, and lately has added some F# to the sauce.
He constantly annoys his friends by talking about software and is passionate about Agile methodologies, which gives him more opportunities to talk annoy his friends even more.
When there are no friends around to annoy, he blogs on CodeCleaners and in his free he time loves go hiking with his wife and two daughters.