57

I have prepared some automatic tests with the Visual Studio Team Edition testing framework. I want one of the tests to connect to the database following the normal way it is done in the program:

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;

But I am receiving an exception in this line. I suppose this is happening because the ConfigurationManager is a singleton. How can you work around the singleton problem with unit tests?


Thanks for the replies. All of them have been very instructive.

Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
yeyeyerman
  • 7,751
  • 7
  • 43
  • 52

4 Answers4

102

Have a look at the Google Testing blog:

And also:

Finally, Misko Hevery wrote a guide on his blog: Writing Testable Code.

Fuhrmanator
  • 11,459
  • 6
  • 62
  • 111
Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
  • 1
    All those references address the problem more deeply that I could sum up in an answer. They are definitely great. – Gregory Pakosz Jan 18 '10 at 12:39
  • 2
    +1! See also the book "Working Effectively with Legacy Code" by Michael Feathers; he provides techniques for testing with Singletons. http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 – TrueWill Jan 18 '10 at 18:18
  • 1
    Especially interesting is the *Performant Singletons* link, leading to a **403 Forbidden** error page. Quite a subtle way to express rejection. – derM - not here for BOT dreams Aug 14 '17 at 12:28
  • 1
    to add a further thought............Unit-Tests do/can run in parallel (think "build server"). And while a global-variable/singleton might be thread-safe, it is not thread-independent. Therefor the tests running in parallel and set/unset the global-variable and each test can screw the others. But thank you for consolidating the arguments against the global variable approach. – granadaCoder Mar 06 '18 at 14:35
  • 1
    for some cases you can't remove signleton, but you still need to test it. So your answer is not complete – Georgiy Chebotarev Nov 09 '20 at 11:27
14

You can use constructor dependency injection. Example:

public class SingletonDependedClass
{
    private string _ProviderName;

    public SingletonDependedClass()
        : this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName)
    {
    }

    public SingletonDependedClass(string providerName)
    {
        _ProviderName = providerName;
    }
}

That allows you to pass connection string directly to object during testing.

Also if you use Visual Studio Team Edition testing framework you can make constructor with parameter private and test the class through the accessor.

Actually I solve that kind of problems with mocking. Example:

You have a class which depends on singleton:

public class Singleton
{
    public virtual string SomeProperty { get; set; }

    private static Singleton _Instance;
    public static Singleton Insatnce
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = new Singleton();
            }

            return _Instance;
        }
    }

    protected Singleton()
    {
    }
}

public class SingletonDependedClass
{
    public void SomeMethod()
    {
        ...
        string str = Singleton.Insatnce.SomeProperty;
        ...
    }
}

First of all SingletonDependedClass needs to be refactored to take Singleton instance as constructor parameter:

public class SingletonDependedClass
{    
    private Singleton _SingletonInstance;

    public SingletonDependedClass()
        : this(Singleton.Insatnce)
    {
    }

    private SingletonDependedClass(Singleton singletonInstance)
    {
        _SingletonInstance = singletonInstance;
    }

    public void SomeMethod()
    {
        string str = _SingletonInstance.SomeProperty;
    }
}

Test of SingletonDependedClass (Moq mocking library is used):

[TestMethod()]
public void SomeMethodTest()
{
    var singletonMock = new Mock<Singleton>();
    singletonMock.Setup(s => s.SomeProperty).Returns("some test data");
    var target = new SingletonDependedClass_Accessor(singletonMock.Object);
    ...
}
bniwredyc
  • 8,649
  • 1
  • 39
  • 52
10

Example from Book: Working Effectively with Legacy Code

Also given same answer here: https://stackoverflow.com/a/28613595/929902

To run code containing singletons in a test harness, we have to relax the singleton property. Here’s how we do it. The first step is to add a new static method to the singleton class. The method allows us to replace the static instance in the singleton. We’ll call it setTestingInstance.

public class PermitRepository
{
    private static PermitRepository instance = null;
    private PermitRepository() {}
    public static void setTestingInstance(PermitRepository newInstance)
    {
        instance = newInstance;
    }
    public static PermitRepository getInstance()
    {
        if (instance == null) {
            instance = new PermitRepository();
        }
        return instance;
    }
    public Permit findAssociatedPermit(PermitNotice notice) {
    ...
    }
    ...
}

Now that we have that setter, we can create a testing instance of a PermitRepository and set it. We’d like to write code like this in our test setup:

public void setUp() {
    PermitRepository repository = PermitRepository.getInstance();
    ...
    // add permits to the repository here
    ...
    PermitRepository.setTestingInstance(repository);
}
Community
  • 1
  • 1
Teoman shipahi
  • 47,454
  • 15
  • 134
  • 158
  • 4
    It seems that something is missing here in the setUp() method. PermitRepository has a private constructor, so you can't use new there ... – leogtzr Dec 26 '16 at 00:17
  • In your setup method, you're calling get instance before setting your test instance. You want to set your test instance first. – Stealth Rabbi Jul 18 '17 at 02:27
5

You are facing a more general problem here. If misused, Singletons hinder testabiliy.

I have done a detailed analysis of this problem in the context of a decoupled design. I'll try to summarize my points:

  1. If your Singleton carries a significant global state, don’t use Singleton. This includes persistent storage such as Databases, Files etc.
  2. In cases, where dependency on a Singleton Object is not obvious by the classes name, the dependency should be injected. The need to inject Singleton Instances into classes proves a wrong usage of the pattern (see point 1).
  3. A Singleton’s life-cycle is assumed to be the same as the application’s. Most Singleton implementations are using a lazy-load mechanism to instantiate themselves. This is trivial and their life-cycle is unlikely to change, or else you shouldn’t use Singleton.
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172