开发者

Mock AutoMapper Mapper.Map call using Moq

Whats the best way to setup a mock expection for the Map function in AutoMapper.

I extract the IMapper interface so I can setup expects for that interface. My mapper has dependencies, so I have to pass those in to the mapper.

What happens when I create 2 instances of my mapper class, with 2 different dependency implementations? I asume that both mappers will use the same dependency instance, since the AutoMapper map is static. Or AutoMapper might even throw an exception because I try to setup 2 different maps with the same objects.?

Whats the best way to solve this?

public interface IMapper {
    TTarget Map<TSource, TTarget>(TSource source);
    void ValidateMappingConfiguration();
}

public class MyMapper : IMapper {
    private readonly IMyService service;

    public MyMapper(IMyService service) {
        this.service = service
        Mapper.CreateMap<MyModelClass, MyDTO>()
            .ForMember(d => d.RelatedData, o => o.MapFrom(s =>
                service.getData(s.id).RelatedData))
    }

    public void ValidateMappingConfiguration() {
        Mapper.AssertConfigurationIsValid();
    }

    public TTarget Map<TSource, TTarget>(TSource source) {
        return Mapper.Map<TSource, TTarget>(source);
    }
}开发者_Go百科


You don't need to mock AutoMapper, you can just inject the real one as explained here:

var myProfile = new MyProfile();
var configuration = new MapperConfiguration(cfg => cfg.AddProfile(myProfile));
IMapper mapper = new Mapper(configuration);

You can inject this mapper in your unit tests. The whole point of using tools like AutoMapper is for you not having to write a lot of mapping code. If you mock AutoMapper you'll end up having to do that.


Whats the best way to setup a mock expection for the Map function in AutoMapper[?]

Here's one way:

var mapperMock = new Mock<IMapper>();
mapperMock.Setup(m => m.Map<Foo, Bar>(It.IsAny<Foo>())).Returns(new Bar());


http://richarddingwall.name/2009/05/07/mocking-out-automapper-with-dependency-injection/

Points out another way of handling dependencies to AutoMapper, which basically will replace my attempt to extract my own IMapper interface. Instead I will attempt to use the existing IMappingEngine as dependency for my classes, to see if that works.


What you need to do is setup AutoMapper like this (StructureMap is IoC). Then you can make your services dependent on IMappingEngine. From there mocking it should be very easy.

public class AutoMapperConfigurationRegistry : Registry
    {
        public AutoMapperConfigurationRegistry()
        {
            ForRequestedType<Configuration>()
                .CacheBy(InstanceScope.Singleton)
                .TheDefault.Is.OfConcreteType<Configuration>()
                .CtorDependency<IEnumerable<IObjectMapper>>().Is(expr => expr.ConstructedBy(MapperRegistry.AllMappers));

            ForRequestedType<ITypeMapFactory>().TheDefaultIsConcreteType<TypeMapFactory>();

            ForRequestedType<IConfigurationProvider>()
                .TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());

            ForRequestedType<IConfiguration>()
                .TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());
        }
    }


The reason you have to invoke automapper config is because, the UNIT Test cases instance runs outside of main application start up files/configs. Hence the auto mapper configuration has to be called and setup before any unit tests start to run. Ideally you place it in TestInitialize methods.


Here is an example of how I provide an instance of AutoMapper as default Mock for IMapper, using AutoFixture and Moq:

Thanks Lucian Bargaoanu for this hint: Actually you can use cfg.AddMaps(params Assembly[]) and Automapper will search for profiles

  1. Create an ICustomization
public class MapperCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Register<IMapper>(() =>
        {
            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.AddMaps(
                    Assembly.Load("BookSharing.Application"),
                    Assembly.Load("BookSharing.Infrastructure"));
            });

            return new Mapper(configuration);
        });
    }
}
  1. Register the customization
fixture.Customize(new MapperCustomization());


following @Dorin Baba answer, I created a useful generic class that can be used to inject any custom mapper in the unit tests

public class MapperCustomization<T> : ICustomization
    where T : class
{
    public void Customize(IFixture fixture)
    {
        fixture.Register<IMapper>(() =>
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddMaps(
                    typeof(T).Assembly);
            });

            return new Mapper(config);
        });
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜