For the last years I’ve been using dependency injection for my service classes. More recently (by the introduction of ASP.NET MVC) that have also been the case for my controller classes. I take in their dependencies via the constructor and then let a Dependency Injection/Inversion of Control (DI/IoC) framework automatically resolve and create the classes. I’ve been using Castle Windsor for this, which has worked really well.
However, when it comes to testing service/controller classes, I’ve previously created (often stubbed) the parameters needed when testing, and then given them manually to the constructor. Like this:
var customerDao = Stub<OrderDao>();
var orderDao = Stub<OrderDao>();
var controller = new CustomerController(customerDao, orderDao);
This has proven to be quite fragile as the dependencies to the service/controller class has changed. Often, the case has been that more dependencies have been added as parameters to the constructor. I know, I know, that might be a code smell that the service/controller should be split into smaller classes… But anyway, code smell or not, the fragility of the tests is a problem.
A while back I read about an AutoMocking container, but thought it was a bit cumbersome to have to use both a Mocking framework and a DI/IoC container in my tests. So, I thought that this should be possibly to accomplish with only a mocking framework and a bit of reflection magic. And it actually turned out to be really easy to do.
As a mocking framework I use Rhino Mocks (btw, I love the new AAA syntax in 3.5!). All my test inherit from a class called BaseTest, which is where I decided to put the autostubbing. The code comes here:
[TestFixture]
public class BaseTest
{
protected static T Stub<T>(params object[] parameters) where T : class
{
return MockRepository.GenerateStub<T>(parameters);
}
protected static T Mock<T>(params object[] parameters) where T : class
{
return MockRepository.GenerateMock<T>(parameters);
}
protected static T CreateAndAutoStubParameters<T>(params object[] parameters)
{
return (T)CreateAndAutoStubParameters(typeof(T), parameters);
}
protected static object CreateAndAutoStubParameters(Type type,
params object[] parameters)
{
var constructor = type.GetConstructors(
BindingFlags.Public | BindingFlags.Instance)[0];
var parameterInfos = constructor.GetParameters();
var constructorParams = GetConstructorParameters(
parameterInfos, parameters);
return constructor.Invoke(constructorParams.ToArray());
}
private static List<object> GetConstructorParameters(ParameterInfo[] parameterInfos,
params object[] parameters)
{
var constructorParams = new List<object>();
foreach (var info in parameterInfos)
{
constructorParams.Add(GetParameter(info, parameters));
}
return constructorParams;
}
private static object GetParameter(ParameterInfo info,
object[] candidateParameters)
{
foreach (var candidate in candidateParameters)
{
if (info.ParameterType.IsAssignableFrom(candidate.GetType()))
{
return candidate;
}
}
return AutoStub(info.ParameterType);
}
protected static T AutoStub<T>() where T : class
{
return (T)AutoStub(typeof(T));
}
protected static object AutoStub(Type type)
{
if (type.IsInterface)
{
return MockRepository.GenerateStub(type);
}
var constructor = type.GetConstructors(
BindingFlags.Public | BindingFlags.Instance)[0];
var parameterInfos = constructor.GetParameters();
var constructorParams = GetConstructorParameters(parameterInfos);
return MockRepository.GenerateStub(type, constructorParams.ToArray());
}
}
So, now let us assume you want to test a CustomerController with dependencies like this:
public class CustomerController
{
public CustomerController(CustomerDao customerDao, OrderDao orderDao)
{
// Code here..
}
// Code here..
}
Then you only have to write this:
var controller = CreateAndAutoStubParameters<CustomerController>();
to create the controller with stubs automatically created as constructor parameters.
Often, you might want to have some control over one of the parameters, and then you could write like this:
var customerDao = AutoStub<CustomerDao>();
customerDao.Stub(c => c.GetById(1)).Return(new Customer());
var controller = CreateAndAutoStubParameters<CustomerController>(customerDao);
This way of testing service and controller classes has removed some of the pain of starting to test classes with dependencies that need to be mocked or stubbed.