Adding Testability Through Mocking Workarounds

Sometimes when you are developing code, you are bound to run into something that is a roadblock from unit testing. That is why frameworks like TypeMock are so great; TypeMock is a powerful mocking tool that can reflect and override any code you feel like mocking, even if that code is internal. If you are using an open source tool like Moq or RhinoMocks, you don’t have the ability to mock non-virtual (non-overridable for the VB crowd) or private methods. TypeMock allows you to do it all. As far as my open-source or free mocking tools go, I like Moq a lot. The interface is simple, straightforward, and is as functional as any of the other frameworks out there. (Note this post isn’t meant to be a selling point for Moq, but it’s useful to know as my examples will be using it.)

As an architect, your bound to run into segments of code that are like the following:

[ExportType(typeof(ICacheLoadingService))]
public class CacheLoadingService : ICacheLoadingService
{
    //A quick wrapper around ASP.NET cache
    private ICache _cache = null;

    //DI constructor injection of relationships
    public CacheLoadingService(ICache cache)
    {
        _cache = cache;
    }
   
    public void Init()
    {
        //instantiate database connector that has a customized constructor that can't be easily mocked
        IDbConnector ctx = new SQLServerDBConnector();
        //Write the commonly used reference data to cache
        _cache.Add(typeof(State), ctx.Get().ToList());
        .
        .
    }

}

Here we have a few things. ICache is a simple wrapper around ASP.NET cache, and is loaded in the constructor of the service (not relevant to this article; just note for reference). Later on, Init() opens up a database connection, which we have no control over creating, but does implement an interface. This is there the challenge for unit testing lies, and we’ll see a workaround very soon. Lastly, [ExportType] is from an open-source project I created to make registration of dependencies very simple.

Back to IDbConnector; with the current setup, using a framework like Moq makes testing this class hard without a live actual database connection. But with a little help, we can add a wrapper around the creation process easier. In walks our little helper:

public interface ICreator
{
   T Create(Func fn);
}

[ExportType(typeof(ICreator))]
public class ObjectCreator : ICreator
{
   public T Create(Func fn)
   {
      return fn();
   }
}

While this solution seems redundant and pointless, this actually solves the problem above. Because we use an object, defining an interface, to create the object, it makes mocking the object that much easier. Changing our init method from above, we add the following to CacheLoadingService:

[ExportType(typeof(ICacheLoadingService))]
public class CacheLoadingService : ICacheLoadingService
{
    private ICache _cache = null;
    private ICreator _creator = null;

    public CacheLoadingService(ICache cache, ICreator creator)
    {
        _cache = cache;
        //passed in through dependency injection
        _creator = creator;
    }
   
    public void Init()
    {
        //ICreator takes the pain out of testing, which you will see next
        IDbConnector ctx = _creator.Create(() => new SQLServerDBConnector());
        _cache.Add(typeof(State), ctx.Get().ToList());
        .
        .
    }

}

Our change is subtle, and it required more code (to create the new class), and a little overhead, sure. But now let’s look at how we can test the DB connection, which wasn’t possible to do earlier. The following is the test logic using Moq:

var creator = new Mock();
creator.Setup(i => i.Get()).Returns(stateTestData);
.
.

var cache = new Mock();
cache.Setup(i => i.Add(typeof(State), It.IsAny<List>()).Verify();

var service = new CacheLoadingService(cache.Object, creator.Object);
service.Init(); //now our DB connection is mocked

cache.Verify();

Now in this test, we verify the DB connection returns the test data and actually gets passed to the cache. This is a very simple way to test objects with external dependencies, like databases, files, etc. Databases hinder unit testing because it breaks the isolation aspect (but is fine for integration tests). And our cache.Verify() statement verifies that everything gets added to the cache as expected.

We managed to take code that was hard to test and provide a simple way of testing it in this article. This is not a one-size-fits-all solution, but it does add testability to a lot of bases you never previously expected, and only adds a small overhead to the application.