What to do About Those Pesky Singletons?

Shout it kick it on DotNetKicks.com

No matter what your opinion is about singletons (I favor against them in most cases), it is important to understand what you can do when you encounter code that is dependent upon them.  Unfortunately, I have come across a lot of code that has abused singletons in a way that makes other code less testable.  My examples here are in C#, but this technique can work in any language.  In some languages, these techniques are easier due to their dynamic capabilities.

I am going to choose a singleton example I have seen in books when the “Singleton Pattern is described – a logger.  Have you seen code like this before?

public class Log
{
    private Log()
    {
        // Set up a way to write to a logging service somewhere
    }

    private static readonly Log theOnlyOne = new Log();
    public static Log Instance
    {
        get { return theOnlyOne; }
    }

    private void WriteToLog(string errorLevel, string category, string message)
    {
        // Write to the logging service
    }

    public void Error(string category, string message)
    {
        WriteToLog("ERROR", category, message);
    }

    public void Warning(string category, string message)
    {
        WriteToLog("WARNING", category, message);
    }

    public void Information(string category, string message)
    {
        WriteToLog("INFORMATION", category, message);
    }
}public class SomeClass
{
    public int Calculate()
    {
        Log.Instance.Information("client", "Doing something...");

        // do calculations
        return 0;
    }
}

Disclaimer: I am not addressing concurrency issues at all in this post.  These techniques work with or without concurrency code.  For the sake of brevity, I left it out.

Of course, if I want to unit-test Calculate(), I have a problem.  Every time I run my test(s), the event log will get written to.  This could be a file, a database, or even a web service.  This type of side-effect behavior is a pretty bad way to be writing my unit tests.  To make matters worse, what happens if I want to validate that Calculate() logged a message appropriately?  Do I read the file, database or web service to validate? 

What I really want here is a test double… but how do I get it in there?  I can:

  1. Re-factor out the singleton
  2. Re-factor the consumer
  3. Put a shim in it
  4. Wrap it

In C#, the first three methods require that the Log be modified.  It either needs to implement an interface, or the methods need to be made virtual.  I prefer to use an interface so that none of the base implementation makes it into the test.  The fourth method can be used in a case where the ability to modify the singleton is prohibited for one reason or another.

The interface for all four strategies is:

public interface ILog
{
    void Error(string category, string message);
    void Warning(string category, string message);
    void Information(string category, string message);
}

Re-Factor Out the Singleton

It is often the case that a singleton does not need to be a singleton. It was just a lazy design decision made a long time ago, but it doesn’t need to be this way.  In fact, when it makes sense to do so, this is my preferred way to deal with singletons.  In this case, I have to ask: why does it need to be a singleton?  There might be a reason, but in most cases, I can just modify the class to not be a singleton anymore and modify the consumer to accept the ILog object via dependency injection:

public class Log : ILog
{
    public Log()
    {
        // Set up a way to write to a logging service somewhere
    }

    // Remove the static instance
    // Remove the Instance getter

    // Everything else the same
}

public class SomeClass
{
    private readonly ILog log;
    public SomeClass(ILog log)
    {
        this.log = log;
    }

    public SomeClass() : this(new Log())
    {}

    public int Calculate()
    {
        log.Information("client", "Doing something...");

        // do calculations
        return 0;
    }
}

Now the test is easy to write.  I can create a test double of any type I want here, just as long as it implements ILog.

[TestFixture]
public class SomeClassTests
{
    [Test]
    public void Test_That_Calculate_Returns_Zero()
    {
        var test = new SomeClass(GetLoggerStub());

        Assert.That(test.Calculate(), Is.EqualTo(0));
    }
}

That is the best way to work with a singleton, in my opinion.  Unfortunately, it is not always that easy.  Maybe it is too much of a refactoring to take the “single” out of the singleton.  Whatever the reason, I need the singleton to stay as a singleton.  The next question:  Do I need my consumer to treat it like a singleton?

Re-factor the Consumer

In this case, the Log class will stay as a singleton, but it is modified slightly to implement ILog:

public class Log : ILog
{
    // Everything else the same
}

Next, the consumer is modified in a similar way to pass the ILog object in via dependency injection:

public class SomeClass
{
    private readonly Func<ILog> log;
    public SomeClass(Func<ILog> log)
    {
        this.log = log;
    }

    public SomeClass() : this(() => Log.Instance)
    { }

    public int Calculate()
    {
        log().Information("client", "Doing something...");

        // do calculations
        return 0;
    }
}

Notice the difference in the way that SomeClass is instantiated now.  Instead of passing a log instance, I pass a function that gets the log instance.  That is because caching a reference to a singleton is a bad idea – the singleton instance has the right to change out from under me.  Instead, the default behavior of SomeClass will ask for the singleton instance every time, but it is no longer required to use the singleton implementation of ILog.  Instead, I can test this in a very similar way (the difference here is subtle, but important… notice that I pass GetLoggerStub as a function, not the result of GetLoggerStub()):

[TestFixture]
public class SomeClassTests
{
    [Test]
    public void Test_That_Calculate_Returns_Zero()
    {
        var test = new SomeClass(GetLoggerStub);

        Assert.That(test.Calculate(), Is.EqualTo(0));
    }
}

Unfortunately, this is not always easy either.  The class I am refactoring may already have a complicated set of constructors.  It may be the case that these are public objects that I don’t want to change right now.  There are several reasons why this might not work for me.  The next option: Leave the Log class as a singleton, but don’t modify the consumer in any way.  Instead, put a shim in the singleton.

Put a Shim in it

This tends to be the technique I use most often, because it creates the least amount of friction when it comes to refactoring the classes for testability.  After all, if there are no tests to validate that I don’t break anything in my re-factoring, I don’t want to make drastic changes.  The consumer doesn’t need to change or know about the changes to the Log class.  It kind of breaks up the singleton pattern a bit (there can be TWO instances!), but all is fair in love and testing, right?  The modified:

public class Log : ILog
{
    private Log()
    {
        // Set up a way to write to a logging service somewhere
    }

    private static ILog theOnlyOne;
    private static ILog testOverride;

    public static ILog Instance
    {
        get
        {
            if (testOverride != null)
                return testOverride;

            if(theOnlyOne == null)
                theOnlyOne = new Log();

            return theOnlyOne;
        }
    }

    public static void OverrideInstanceForTesting(ILog log)
    {
        testOverride = log;
    }

    // Everything else the same
}

Again, I don’t have to modify my consumer class, but my test fixture has to do a bit more work:

public class SomeClassTests
{
    [SetUp]
    public virtual void SetUp()
    {
        Log.OverrideInstanceForTesting(GetLoggerStub());
    }

    [TearDown]
    public virtual void TearDown()
    {
        Log.OverrideInstanceForTesting(null);
    }

    [Test]
    public void Test_That_Calculate_Returns_Zero()
    {
        var test = new SomeClass();

        Assert.That(test.Calculate(), Is.EqualTo(0));
    }
}

This option might not be the available either.  If the singleton is a publically consumed service and I can’t modify it for some reason (I don’t own it, for instance), then I can wrap it.

Wrap it

This is a technique I use when Microsoft has given me a static class or singleton that I don’t want a direct dependency to, but they also didn’t give me an interface to substitute in either.  I just create my own interface that looks a lot like the singleton and wrap it!

public class LogWrapper : ILog
{
    public void Error(string category, string message)
    {
        Log.Instance.Error(category, message);
    }

    public void Warning(string category, string message)
    {
        Log.Instance.Warning(category, message);
    }

    public void Information(string category, string message)
    {
        Log.Instance.Information(category, message);
    }
}

With that, I can use the same modification to the consumer as in “Re-Factor Out the Singleton”.  The test also looks the same as “Re-Factor Out the Singleton”.

In Summary

There are other ways to deal with this problem.  I could use TypeMock Islolator to replace the singleton implementation with a new one.  I have even seen some suggestions that you can use a post-compiler code injection library to replace the singleton implementation.  These, to me, are cheating.  They get around a problem that you have in your system: tight coupling.  I much prefer the strategies mentioned in this post for dealing with singletons because they allow you to make baby steps towards loose coupling.  Over time, these re-factorings add up and push you in the direction of having testable code.

One Response to “What to do About Those Pesky Singletons?”

  1. thinhduong says:

    You could achieve the Log singleton and test double. By using IoC, you register Log implemeted ILog and tell Ioc register it as single, so you can achieve two goals

Leave a Reply