Monday, May 28, 2012

Using NUnit’s Parametrized Tests to test different implementations of a service registered in an IoC container

There is a service:

public interface IFoo
{
    int DoFoo();
}

and at least two different implementations of it:

public class Bar : IFoo
{
    #region IFoo Members
 
    public int DoFoo()
    {
        return 1;
    }
 
    #endregion
}
 
public class Qux : IFoo
{
    #region IFoo Members
 
    public int DoFoo()
    {
         return 2;
    }
 
    #endregion
}

We often use an IoC container to bind the implementation to the abstraction, some of us even use a service locator to resolve services at runtime:

IUnityContainer container = new UnityContainer();
container.RegisterType<IFoo, Bar>();
 
UnityServiceLocator locator = new UnityServiceLocator( container );
ServiceLocator.SetLocatorProvider( () => locator );

Having at least two different implementations, I would like to be able to test them against some common criteria like “does a method always returns values”.

First approach – inheritance on test classes

My first approach to test different implementations was to create a base abstract test class and inherit it everytime I need to test another implementation:

public abstract class TestBase
{
    [SetUp]
    public virtual void SetUp()
    {
        IUnityContainer   container = new UnityContainer();
        UnityServiceLocator locator = new UnityServiceLocator( container );
 
        ServiceLocator.SetLocatorProvider( () => locator );
    }
 
    [Test]
    public void Foo()
    {
        var foo = ServiceLocator.Current.GetInstance<IUnityContainer>().Resolve<IFoo>();
 
        Assert.That( foo.DoFoo(), Is.GreaterThan( 0 ) );
    }
}
 
[TestFixture]
public class BarTest : TestBase
{
    public override void SetUp()
    {
        base.SetUp();
 
        ServiceLocator.Current.GetInstance<IUnityContainer>().RegisterType<IFoo, Bar>();
    }
}
 
[TestFixture]
public class QuxTest : TestBase
{
    public override void SetUp()
    {
        base.SetUp();
 
        ServiceLocator.Current.GetInstance<IUnityContainer>().RegisterType<IFoo, Qux>();
    }
}

This way I can see an extra branch for each implementation in the test runner and I don’t have to copy-paste the test method body since it is automatically inherited in test classes:

Second approach – parametrized tests

The former approach has a significant disadvantage – everytime I create a new implementation, I have to create a new test subclass. I will use then a Nunit feature called “Parametrized tests” which allows me to inject a given set of parameters to a test method, one value after the other, and still be able to see all tests in the test runner.

[TestFixture]
public class TestBase
{
    [SetUp]
    public virtual void SetUp()
    {
        IUnityContainer   container = new UnityContainer();
        UnityServiceLocator locator = new UnityServiceLocator( container );
 
        ServiceLocator.SetLocatorProvider( () => locator );
    }
 
    public IFoo[] IFoos =
    {
        new Bar(),
        new Qux()
    };
 
    [TestCaseSource( "IFoos" )]
    public void Foo( IFoo foo )
    {
        ServiceLocator.Current.GetInstance<IUnityContainer>().RegisterInstance<IFoo>( foo );
 
        Assert.That( foo.DoFoo(), Is.GreaterThan( 0 ) );
    }
}

Note that there are few differences. First, the test class is no longer abstract. Then, the implementation of the service is injected to the test method and because of the TestCaseSource attribute, I inform NUnit where to look for values ready to be injected. When the test starts, I register the injected instance in my container so that when the service implementation asks again for the implementation bound to the service interface, it will get the same object.

Note also that this approach still allows me to select a test in the test runner:

Happy coding.

No comments: