Share via


Unit Testing of Singleton Objects

Overview

Over the years, there has been a bustling debate among software developers on the use of the singleton pattern.  Some would say it is better in the sense that it has complete control of the object instantiation and ensures that there is one and only one instance of the class, but others feel that it is an anti-pattern since the instantiation part should not be the responsibility of the class itself. We could arm wrestle on each of these individual points all day and not come up with something more conclusive, but in this article I would like to put the questions “How do we unit test a singleton object?” and “How do we make singleton classes unit testable?” as the green and red cherry spots in the dart board.

Let’s get started with testing a singleton class that is dependent on another singleton class.



  public class  DependentObject
  {
        private static  readonly Lazy<DependentObject> _instance =  new  Lazy<DependentObject>(() => new DependentObject());
        public static  DependentObject Instance { get { return _instance.Value; } }
        private DependentObject()
        {
            Initialize();
        }
 
        public string  GetSampleObjectFullName()
        {
            return SampleSingleton.Instance.GetFullName();
        }
        private void  Initialize()
        {
            //do initialization processes
        }
  }
 
  public sealed  class SampleSingleton : BaseObject
  {
        private static  readonly Lazy<SampleSingleton> _instance =  new  Lazy<SampleSingleton>(() => new SampleSingleton());
        public static  SampleSingleton Instance { get { return _instance.Value; } }
        private SampleSingleton()
        {
            ID = 0;
            FirstName =   "John"  ;
            LastName =   "Doe"  ;
        }
        public string  GetFullName()
        {
            return string.Concat(FirstName, " ", LastName);
        }
 
 
        public void  ChangeFirstName(string newName)
        {
            FirstName = newName;
        }
  }

 

Unit Testing in Action

Now, let’s go ahead and test the DependentObject class.

 

[TestMethod]
public void  Test_GetFullName()
{
    string name = "John Doe";
    Assert.AreEqual(name,DependentObject.Instance.GetSampleObjectFullName());
}

A mediocre can say this is good enough as a unit test as we can verify here if the method inside the class returns the expected result. Some people might agree with this but the bigger question here is “Are we really unit testing” in this particular case? If you refer back to the main goal of unit testing, it says we want to isolate each component or class as an independent unit and prove that the individual parts that make it as a whole are all working correctly.

In the above example, can we say that we have satisfied the main goal for unit testing? Have we isolated the class as an independent unit? Can we prove that it only performs what is expected from it? In order to answer these questions, we need to rip open the frog and see what is inside and then analyze how each of the parts is working with each other. Yes, it doesn’t sound like fun but most of the time I say you have to be a man and do the right thing.

So we have two singleton classes, DependentObject and SampleSingleton. The DependentObject contains a method GetSampleObjectFullName which in turn uses the SampleSingleton instance to invoke its GetFullName method and return the value. From the unit testing perspective, we want to test the GetSampleObjectFullName method.

public string  GetSampleObjectFullName()
{
    return SampleSingleton.Instance.GetFullName();
}

If you first look at the method, you would say its responsibility is to return the sample object’s fullname but it’s not. For me, its sole responsibility is to invoke the GetFullName method of the SampleSingleton instance and then return whatever the result is. If you can recall encapsulation and single responsibility principles, the DependentObject does not and should not have any visibility into how the invoked method is working internally nor if the returned value is correct or not. In our current code, we cannot isolate the two classes independently because they have strong coupling between each other. We have to re-design them in such a way that we can create true and efficient unit test methods. On the other hand, imagine re-engineering the frog and be able to alter the functions of its internal parts and replace it with whatever we come up with every time we finish our glass of dirty martini. J(Now you say,  Oh!, this looks interesting now.)

Welcome to the World of Interfaces and Mocking

To remove the hard-coded dependencies between classes, we will re-design our example to follow an interface based design and replace the interface dependencies with whatever we can think of later. The interfaces and classes will now look like the code shown below:

public interface  ISampleImpl
{
    string GetFullName();
    void ChangeFirstName(string newName);
}
 
public sealed  class SampleSingleton : BaseObject, ISampleImpl
{
    private static  readonly Lazy<ISampleImpl> _instance =  new  Lazy<ISampleImpl>(() => new SampleSingleton());
    public static  ISampleImpl Instance { get { return _instance.Value; } }
    private SampleSingleton()
    {
        ID = 0;
        FirstName = "John";
        LastName = "Doe";
    }
    public string  GetFullName()
    {
        return string.Concat(FirstName, " ", LastName);
    }
 
 
    public void  ChangeFirstName(string newName)
    {
        FirstName = newName;
    }
}

 

 

public interface  IDependentObject
{
    string GetSampleObjectFullName();
    ISampleImpl SampleObject { get; set; }
}
 
public class  DependentObject : IDependentObject
{
    private static  readonly Lazy<IDependentObject> _instance =  new  Lazy<IDependentObject>(() => new DependentObject());
    public static  IDependentObject Instance { get { return _instance.Value; } }
    private DependentObject()
    {
        SampleObject = SampleSingleton.Instance;
        Initialize();
    }
 
    public string  GetSampleObjectFullName()
    {
        return SampleObject.GetFullName();
    }
    private void  Initialize()
    {
        //do initialization processes
    }
 
    public ISampleImpl SampleObject
    {
        get;
        set;
    }
}

 

You will notice that not much of the actual implementation code has changed. We just modified the structure and have the classes inherit from a common interface so that we can independently isolate one another when we create the “true” unit test. In our example, we need to isolate the SampleSingleton class from the DependentObject class and we can do that by replacing the SampleObject property with a mocked object. This practice is called Dependency Injection. There are various forms of Dependency Injection(DI) and since we exposed a public property to inject a new implementation, this particular type is called Property Injection. I also mentioned that we are going to use a mocked object in our testing. For this example, we will utilize a mocking framework called Moq.

The True Unit Test

[TestMethod]
        public void  Test_That_GetSampleObjectFullName_Invokes_ISample_GetFullName()
        {
            var mockedSample = new  Moq.Mock<ISampleImpl>();
            IDependentObject obj2Test = DependentObject.Instance;
             
     obj2Test.SampleObject = mockedSample.Object; //use of property injection
             
             
            mockedSample.Setup(x => x.GetFullName());
            obj2Test.GetSampleObjectFullName();
 
            mockedSample.VerifyAll();
          
        }

 

If you will compare this from the first unit test example, your first reaction would be, “Does that mean I have to write more code”?  Yes, you might end up writing more. It is because you need to isolate the class you are testing from other components that it is dependent with. In unit testing, that is the responsibility of the mocking framework. Going back to our objective, we only need to verify if the GetSampleObjectFullName method really invokes the GetFullName method of the ISampleImpl interface.  The good news is that the mocking framework has the facility to setup those expectations within your unit test. In the example above, we first created the mocked object for the ISampleImpl interface. We then instantiated our object to be tested and that remains a singleton instance. The third line is the part where we replaced the SampleSingleton instance in the DependentObject with a mocked object.  We then call the Setup method of the mocking framework to specify that we are expecting a method named “GetFullName” will be invoked by our method under testing. Finally, we concluded our test by checking if all of our expectations are met.

By executing the test, the result looks like this:

1 passed, 0 failed, 0 skipped, took 0.59 seconds (Ad hoc).

 

By modifying the code not to invoke the method,

public string  GetSampleObjectFullName()
{
    return "John Doe";//SampleObject.GetFullName();
}

 

 The new test result looks like this:

Test 'M:Singleton.Test.TestDependentObject.Test_That_GetSampleObjectFullName_Invokes_ISample_GetFullName' failed: The following setups were not matched:

ISampleImpl x => x.GetFullName()

 

       Moq.MockVerificationException: The following setups were not matched:

       ISampleImpl x => x.GetFullName()

      

       at Moq.Mock.VerifyAll()

       TestDependentObject.cs(24,0): at Singleton.Test.TestDependentObject.Test_That_GetSampleObjectFullName_Invokes_ISample_GetFullName()

 

0 passed, 1 failed, 0 skipped, took 0.72 seconds (Ad hoc).

 

Further Analysis

You might ask, why did I use property injection instead of constructor injection?  Due to the nature of singletons which are sealed and doesn’t have publicly accessible constructor we cannot use the mocking framework and we will have no way of replacing the dependency object and isolate our target object.

Wrapping it up

I hope this article has helped you in some way in understanding the challenges with unit testing singleton objects, how to decouple your classes by following an interface based design to make them independent and testable and lastly how you would use mocking frameworks to implement the isolation and setup unit test expectations.

Happy Unit Testing!

Mabuhay!