How to Test your Production Code: Using Dummy Objects

Let’s pretend we want to add a method to a class. We, of course, are going implement the method using TDD but we have a problem, to create an instance of the class we need to pass in a super-difficult-to-build object. We are sure that our implementation won’t touch this object in any way, like if it smelled funny, but the class needs an’instance of that object. What can we do?

To understand what can be done let’s look at this test:

[Test]
public void GetInvoiceAsPdf(){
    // some pretty nasty code to intanciate the dbFacade object
    var dbFacade = new DbFacade( /* bleh */);
    // ....
    // A lot of properties that need to be setup for dbFacade to function

    var formatter = new InvoiceFormatter(dbFacade);
    // The test code
}

All the horrible code needed to instantiate a working instance of dbFacade is going to obscure the real purpose of the test and make the tests fragile because if something in the procedure of constructing the object changes, a lot of tests are going to fail.

But, do we really need a working instance of DbFacade? we said earlier that we are not going to use it in any way in our new method, so for what we know we could completely avoid it in our tests and just pass a null to the constructor of InvoiceFormatter .

[Test]
public void GetInvoiceAsPdf(){
    var formatter = new InvoiceFormatter(null);
    // The test code
}

If the constructor of InvoiceFormatter doen’t check for nulls this is a perfectly acceptable strategy and the great thing is that it cleans up the test function. Of course, if we are dealing with a constructor that doens’t accept passing nulls, we have to try to find a different solution.

One solution can be to check if we can instantiate a DbFactory in a simpler way, say with the default constructor and without setting any properties. This won’t result in a working instance of the class but it will be enought for our tests.

[Test]
public void GetInvoiceAsPdf(){
    // using default constructor to build a dummy object
    var dbFacade = new DummyDbFacade();
    var formatter = new InvoiceFormatter(dbFacade);
    // The test code
}

Remember, we don’t need a working DbFacade because we are not going to use it in our tests.

If DbFacade does not expose a default constructor we could inherit from it (if it’s not sealed) and provide one with a dummy class.

private class DummyDbFacade : DbFacade
{
    public DummyDbFacade() {}
}
[Test]
public void GetInvoiceAsPdf(){
    // using default constructor to build a dummy object
    var dbFacade = new DummyDbFacade();
    var formatter = new InvoiceFormatter(dbFacade);
    // The test code
}

Or we could implement the same interface as DbFacade if it implements an interface and InvoiceFormatter requires the same one and not a concrete class.

private class DummyDbFacade : IDbFacade
{
    public DummyDbFacade() {}
// empty methods if IDbFacade
}
[Test]
public void GetInvoiceAsPdf(){
    // using default constructor to build a dummy object
    var dbFacade = new DummyDbFacade();
    var formatter = new InvoiceFormatter(dbFacade);
    // The test code
}

Another use of a dummy object is when we are covering a method already implemented and we only need to provide the values for the path that we want to test. For example the method LoadInvoicesByDate receives an IDbFactory

public void LoadInvoicesByDate (DateTime from, DateTime to, DateTime threshold, IDbFactory dbFactory){
    if(from > threshold)
        throw new DateOverThresholdException("from");

    // load all invoices in the timespan given using dbFactory
}

If we are going to test that a DateOverThresholdException is thrown when from is higher than threshold we are free to pass a null as an argument for dbFactory.

To summarize, Dummies are used to put the code that you are going to test in a testable condition whenever you don’t need to read any information form them or call their functions. For that you have stubs, spies, mocks and fakes…

Advertisements

7 thoughts on “How to Test your Production Code: Using Dummy Objects

  1. Pingback: How to Test your Production Code: Using Test Stubs – Code cleaners

  2. Pingback: How To Test Your Production Code: Test Spy – Code cleaners

  3. Pingback: How to Test your Production Code: Everything You Need To Know About Test Doubles – Code cleaners

  4. Before anything else, thanks for explaining how to include TDD in complicated, real-world examples.

    I have a question about inheriting complicated object to create dummies. The example in the blog is below the text “If DbFacade does not expose a default constructor we could inherit from…”.

    If we can’t use the default DbFacade constructor, the suggestion is to inherit from the class and to create DummyDbFacade. But if we don’t have the default DbFacade constructor, we are forced to use some other DbFacade constructor.

    To simplify… If we have:

    public class DbFacade {
    // Wanting an instance of ComplicatedClass and hiding the default constructor.
    public DbFacade(ComplicatedClass obj) { }
    }
    public class DummyDbFacade : DbFacade {
    public DummyDbFacade() { }
    }

    Compiler is merciless 🙂 and it will warn us: “DfBacade does not containt the constructor that takes 0 arguments.”

    In fact, the child constructor wants something like:
    public DummyDbFacade() : base (objComplicatedClass) {}

    But with this, we are at the beginning of the problem.
    Copy-paste from the blog:
    // some pretty nasty code to intanciate the dbFacade object
    var dbFacade = new DbFacade( /* bleh */);

    Am I missing something? Is there a way around this?
    Thanks!

    Like

  5. Pingback: How To Test Your Production Code: Test Spy – Code Cleaners Blog

  6. Pingback: How to Test your Production Code: Using Test Stubs – Code Cleaners Blog

  7. Pingback: How to Test your Production Code: Everything You Need To Know About Test Doubles – Code Cleaners Blog

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