使用Moq模拟AutoMapper的Mapper.Map调用

47
什么是设置Automapper中Map函数的最佳模拟预期的方法?
我提取了IMapper接口以便可以为该接口设置预期。我的映射器具有依赖项,因此必须将其传递给映射器。
当我创建两个mapper类的实例,并使用两个不同的依赖项实现时,会发生什么?我假设两个映射器都将使用相同的依赖项实例,因为Automapper map是静态的。或者Automapper甚至可能会抛出异常,因为我尝试使用相同的对象设置两个不同的映射?
解决这个问题的最佳方法是什么?
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);
    }
}
7个回答

67

你不需要模拟AutoMapper,可以按照这里的说明注入真实的AutoMapper:

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

你可以将这个映射器注入到你的单元测试中。使用AutoMapper之类的工具的重点在于你不需要编写大量的映射代码。如果你模拟AutoMapper,那么最终还是需要编写这些代码。



我仍然可以打开它。 - ataravati
奇怪。看起来我的ISP正在阻止该网站? 错误522 Ray ID:5798fed7d802d24c • 2020-03-25 13:32:00 UTC连接超时 如果您可以通过将其导出到时间机器来帮助我查看它,那就太好了。 - SpiritBob
1
同意。每个模型中的模拟映射器都是浪费时间,除非您需要检查映射器配置文件是否正确。 - wtf512
1
如果你了解最佳实践,这应该是被接受的答案。你希望能够模拟服务/客户端响应,而不是映射响应,让你的模拟流经实际的映射逻辑,然后测试结果。 - heug

51

如何为AutoMapper中的Map函数设置模拟期望的最佳方法?

下面是一种方法:

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

3
只是一个小的补充。mapperMock.Setup(m => m.Map<Foo, Bar>(It.IsAny<Foo>())).Returns((Foo src) => new Bar(){ Value = src.Value});这段代码意思是在进行单元测试时,使用mapperMock模拟一个Map方法,用于将类型为Foo的对象映射成类型为Bar的对象。当该方法被调用时,它会返回一个新的Bar实例,其中Value属性的值等于传入的Foo实例的Value属性的值。 - John Nyingi
1
这个解决方案很好,但请使用@ataravati的下面的答案,它可以节省很多时间。 - Raghuveer

3

2
您需要做的是像这样设置AutoMapper(StructureMap是IoC)。然后您可以使您的服务依赖于IMappingEngine。从那里开始模拟它应该很容易。
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>());
        }
    }

0

在@Dorin Baba的回答之后,我创建了一个有用的通用类,可以用于在单元测试中注入任何自定义映射器

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
这是一个关于如何提供AutoMapper实例作为IMapper的默认Mock的示例,使用AutoFixture和Moq:
感谢Lucian Bargaoanu提供的提示:实际上你可以使用cfg.AddMaps(params Assembly[]),这样Automapper会搜索配置文件。
具体步骤如下:
1. 创建一个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. 注册自定义设置
fixture.Customize(new MapperCustomization());

https://docs.automapper.org/en/latest/Configuration.html?#assembly-scanning-for-auto-configuration - Lucian Bargaoanu
1
谢谢你!老实说,我不知道 :) 感谢兄弟。 - Dorin Baba

0
你需要调用automapper配置的原因是,单元测试实例运行在主应用程序启动文件/配置之外。因此,在任何单元测试开始运行之前,必须调用和设置自动映射器配置。理想情况下,你应该将它放在TestInitialize方法中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接