So the scenario is, I have a singleton manager class( I know what you are thinking..bad design.. agree… I have excuses..).
For my tests I need to inject a mock object to a property on this singleton instance. I don't want to make major design or code changes to production code, which is working fine now.
My solution was to use Unity to do the property injection. This is easy with a non-singleton class, I am not using configuration files for Unity.
The sample below is the proof of my solution.
This is the singleton manager
1: public class ConnectionManager : IConnectionManager
2: {
3: private static IConnectionManager _instance;
4:
5: private ConnectionManager()
6: {
7: //The prod code always use the concrete implementation(default)
8: ConfigStore = new UserConfig();
9: }
10:
11: public static IConnectionManager Instance
12: {
13: get
14: {
15: //simple singleton
16: return _instance ?? (_instance = new ConnectionManager());
17: }
18: }
19:
20: //The property needs to be injected
21:
22: public IUserConfig ConfigStore { get; set; }
23:
24:
25: public bool CanConnect()
26: {
27: //if config exits then it can connect
28: return ConfigStore.ConfigExists();
29: }
30: }
I have extracted an interface of this class(the other way around)
public interface IConnectionManager
{
IUserConfig ConfigStore { get; set; }
bool CanConnect();
}
This is the type of the property
public interface IUserConfig
{
bool ConfigExists();
}
and default implementation of the property that the production code will use
class UserConfig : IUserConfig
{
public bool ConfigExists()
{
Console.WriteLine("Checking config exists");
return false;
}
}
In my test fixture I am creating a mock for the IUserConfig and registering that instance in the container.
var userConfigMock = new Mock<IUserConfig>();
//mocking the IUserConfig and returns true on ConfigExits call
userConfigMock.Setup(u => u.ConfigExists()).Returns(true);
//register the mock in container
_container.RegisterInstance<IUserConfig>(userConfigMock.Object);
Now registering interface for the singleton mentioning it to user property injector which the container will resolve.
_container.RegisterType<IConnectionManager>(new InjectionProperty("ConfigStore",
new ResolvedParameter<IUserConfig>()));
Using the BuidUp extension of Unity I am asking the container to attach to an existing instance.
IConnectionManager singletonInstance = ConnectionManager.Instance;
IConnectionManager connectionManager = _container.BuildUp(singletonInstance);
The full test fixture is below
1: public class ConnectionManagerFixture
2: {
3: private IUnityContainer _container;
4:
5: [SetUp]
6: public void Setup()
7: {
8: _container = new UnityContainer();
9:
10: var userConfigMock = new Mock<IUserConfig>();
11: //mocking the IUserConfig and returns true on ConfigExits call
12: userConfigMock.Setup(u => u.ConfigExists()).Returns(true);
13:
14: //register the mock in container
15: _container.RegisterInstance(userConfigMock.Object);
16:
17: _container.RegisterType<IConnectionManager>(new InjectionProperty("ConfigStore",
18: new ResolvedParameter<IUserConfig>()));
19: }
20:
21: [Test]
22: public void Tester()
23: {
24: IConnectionManager singletonInstance = ConnectionManager.Instance;
25: IConnectionManager connectionManager = _container.BuildUp(singletonInstance);
26: //the mock return true.
27: Assert.IsTrue(connectionManager.CanConnect());
28: }
29: }
Remember this is a sample I used to prove my solution.
Things to note here are
- The application design has minimum impact, its still using singleton etc. I have extracted an Interface, and in this case I have changed field to a auto property.
- If you are using Unity with mapping configuration you don’t have to use the InjectionProperty you can specify them in the configuration.
- While registering the singleton type, you have to use the Interface instead of class.
Hope this helps…
Feel free to ask question if you have any…