简体   繁体   English

使用 Moq 模拟 AutoMapper Mapper.Map 调用

[英]Mock AutoMapper Mapper.Map call using Moq

Whats the best way to setup a mock expection for the Map function in AutoMapper.在 AutoMapper 中为 Map function 设置模拟期望的最佳方法是什么。

I extract the IMapper interface so I can setup expects for that interface.我提取了 IMapper 接口,因此我可以为该接口设置预期。 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?当我创建映射器 class 的 2 个实例并使用 2 个不同的依赖项实现时会发生什么? 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.?我假设两个映射器将使用相同的依赖实例,因为 AutoMapper map 是 static。或者 AutoMapper 甚至可能抛出异常,因为我尝试使用相同的对象设置 2 个不同的映射。?

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);
    }
}

Whats the best way to setup a mock expection for the Map function in AutoMapper[?]在 AutoMapper[?] 中为 Map 函数设置模拟期望的最佳方法是什么?

Here's one way:这是一种方法:

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

You don't need to mock AutoMapper, you can just inject the real one as explained here :您不需要模拟 AutoMapper,您可以按照此处的说明注入真正的 AutoMapper:

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.使用 AutoMapper 等工具的全部意义在于您不必编写大量映射代码。 If you mock AutoMapper you'll end up having to do that.如果您模拟 AutoMapper,您最终将不得不这样做。

http://richarddingwall.name/2009/05/07/mocking-out-automapper-with-dependency-injection/ 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.指出了另一种处理对 AutoMapper 的依赖关系的方法,它基本上将取代我尝试提取自己的 IMapper 接口的尝试。 Instead I will attempt to use the existing IMappingEngine as dependency for my classes, to see if that works.相反,我将尝试使用现有的 IMappingEngine 作为我的类的依赖项,以查看是否有效。

What you need to do is setup AutoMapper like this (StructureMap is IoC).您需要做的是像这样设置 AutoMapper(StructureMap 是 IoC)。 Then you can make your services dependent on IMappingEngine.然后你可以让你的服务依赖于 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.您必须调用 automapper 配置的原因是,UNIT 测试用例实例在主应用程序启动文件/配置之外运行。 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.理想情况下,您将它放在 TestInitialize 方法中。

Here is an example of how I provide an instance of AutoMapper as default Mock for IMapper, using AutoFixture and Moq:这是我如何使用 AutoFixture 和 Moq 提供 AutoMapper 实例作为 IMapper 的默认 Mock 的示例:

Thanks Lucian Bargaoanu for this hint: Actually you can use cfg.AddMaps(params Assembly[]) and Automapper will search for profiles感谢 Lucian Bargaoanu 的提示:实际上您可以使用cfg.AddMaps(params Assembly[])并且 Automapper 将搜索配置文件

  1. Create an ICustomization创建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按照@Dorin Baba 的回答,我创建了一个有用的通用 class,它可用于在单元测试中注入任何自定义映射器

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);
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM