Agile Legacy Code

Primary Menu

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]

publicvoidGetInvoiceAsPdf(){

// some pretty nasty code to intanciate the dbFacade object

vardbFacade=newDbFacade( /* bleh */);

// ....

// A lot of properties that need to be setup for dbFacade to function

varformatter=newInvoiceFormatter(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]

publicvoidGetInvoiceAsPdf(){

varformatter=newInvoiceFormatter(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]

publicvoidGetInvoiceAsPdf(){

// using default constructor to build a dummy object

vardbFacade=newDummyDbFacade();

varformatter=newInvoiceFormatter(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.

privateclassDummyDbFacade : DbFacade

{

publicDummyDbFacade() {}

}

[Test]

publicvoidGetInvoiceAsPdf(){

// using default constructor to build a dummy object

vardbFacade=newDummyDbFacade();

varformatter=newInvoiceFormatter(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.

privateclassDummyDbFacade : IDbFacade

{

publicDummyDbFacade() {}

// empty methods if IDbFacade

}

[Test]

publicvoidGetInvoiceAsPdf(){

// using default constructor to build a dummy object

vardbFacade=newDummyDbFacade();

varformatter=newInvoiceFormatter(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

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…

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.

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.