Testing code that use ServiceLocator: ServiceLocatorStub

This a common question, and the answer usually is that this kind of code is harder to test. Really? For me this is not so true.

This is an example test:

[Test]
public void do_something_save_the_object_properly()
{
    var daoMock = new DaoMock();

    //initializing my servicelocator stub.
    ServiceLocatorStub.Create().AddInstance<IDao>(daoMock);

    //this component use service locator internally
    var component = new AComponent(); 
    component.DoSomething();

    Assert.AreEqual("hey", daoMock.WasCalledWith); 

}

The implementation of DoSomething look as follows:

public void DoSomething()
{
    ServiceLocator.Current.GetInstance<IDao>().Save("hey");
}

The “DaoMock” implementation is not so usefull, but:

public class DaoMock : IDao
{
    public string WasCalledWith { get; private set; }
    public void Save(string anObject)
    {
        WasCalledWith = anObject;
    }
}

And finally here is the ServiceLocatorStub:

public class ServiceLocatorStub : ServiceLocatorImplBase 
{
    private readonly IDictionary<Type, ICollection<object>> 
        registeredTypes = new Dictionary<Type, ICollection<object>>();

    private ServiceLocatorStub()
    {}

    public ServiceLocatorStub AddInstance<TService>(TService instance)
    {
        ICollection<object> instanceCollection;
        if(!registeredTypes.TryGetValue(typeof(TService), out instanceCollection))
        {
            instanceCollection = new List<object>();
            registeredTypes[typeof (TService)] = instanceCollection;
        }
        instanceCollection.Add(instance);
        return this;
    }

    public static ServiceLocatorStub Create()
    {
        var fakeServiceLocator = new ServiceLocatorStub();
        ServiceLocator.SetLocatorProvider(() => fakeServiceLocator);
        return fakeServiceLocator;
    }

    protected override object DoGetInstance(Type serviceType, string key)
    {
        return registeredTypes[serviceType].FirstOrDefault();
    }

    protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
    {
        return registeredTypes[serviceType];
    }
}

The constructor of the class is private, you must call Create(), this will return a new instance and set this instance as the current service locator. Then you can add services (mocks or stubs) fluently!

You don't need to reference a concrete container, neither the implementation of the service locator for the container.


blog comments powered by Disqus
  • Categories

  • Archives