The service layer of my ASP.NET MVC 3 application uses AutoMapper to map view models to business objects. I implemented type converters to convert object IDs submitted in post/get request to business objects.Type converters look up entities in the database by IDs and rebuild business from those. For example:
class UserViewModel {
// user fields
Guid? Manager; // this is an ID of another user in the system
}
A service will call AutoMapper:
var userModel = Mapper.Map<UserViewModel, UserModel>(viewModel);
to map to this class:
class UserModel {
// other fields...
UserModel Manager;
}
with the help of type converter that looked up another user in the database by Manager GUID from view model. Type converters are injected with dependencies using Ninject and everything works fine in the web application.
I’m trying to write unit-test that will use mock repositories with type converters. AutoMapper can be configured to construct services using a user provided function:
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(type => Kernel.GetService(type));
});
In web application Kernel is Ninject, for tests I decided to provided my own method that returns instances of requested types from a dictionary:
Dictionary<Type, object> typeDict = new Dictionary<Type, object>()
{
{ typeof(IRepository), new MockRepository() }
};
...
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(type => { return typeDict[type]; } );
});
The idea was that when AutoMapper is configured to convert a Guid to an object like this:
Mapper.CreateMap<Guid?, TDropDown>()
.ConvertUsing<GuidToSelectListValueConverter<TDropDown>>();
and needs to create an new instance of GuidToSelectListValueConverter and inject it with a repository it will request the IRepository type using method configured in ConstructServicesUsing.
Instead AutoMapper actually tries to obtain an instance of GuidToSelectListValueConverter type. Which means I become responsible for creating a new instance of type converter and injecting it with dependencies. My typeDict should look like this instead:
Dictionary<Type, object> typeDict = new Dictionary<Type, object>()
{
{ typeof(GuidToSelectListValueConverter), new GuidToSelectListValueConverter(
new MockRepository()) }
};
I do have a lot of type converters and manually write resolver functions for those doesn’t seem feasible. So that got me thinking. Would it be wrong to simply configure Ninject for to resolve these dependencies in my unit test? I could bind IRepository to MockRepository and then when I need an instance of service simply call Kernel.GetService(typeof(MyService)) and have Ninject inject it with MockRepository. The added benefit is that AutoMapper will also use Ninject to get instances of type converters and value resolvers:
// configure Ninject
Kernel.Bind<IRepository>().To<MockRepository>();
// initialize AutoMapper
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(t => Kernel.GetService(t));
});
// create an instance of service to test
var service = Kernel.GetService(IMyService);
// do work
var result = service.DoWork();
// analyze result
Now when DoWork method calls Mapper.Map type converters are created using Ninject.
Any suggestions, ideas and best practices advices are welcome.
Thank you!
Check out this post on using IoC with AutoMapper.
This helped me get going in the right direction by using
IMappingEngineinstead ofMapperin my controllers.