单元测试自动映射器

Unit testing automapper

我无法让自动映射器在我的单元测试中工作。我正在注入映射引擎,这在代码中运行良好,但在测试中却不行。这是我的测试设置和测试。我正在使用 Moq 来模拟地图引擎。

private static IDbContext Context { get; set; }
private static Mock<IMappingEngine> Mapper { get; set; }
private static Guid MenuId { get; set; }

private static Guid menuItem1Id { get; set; }
private static Guid menuItem2Id { get; set; }
private static Guid menuItem3Id { get; set; }

[ClassInitialize]
public static void SetUp(TestContext context)
{
    MenuId = Guid.NewGuid();
    Context = new TestDbContext();

    menuItem1Id = Guid.NewGuid();
    menuItem2Id = Guid.NewGuid();
    menuItem3Id = Guid.NewGuid();

    var contentPage1 = new ContentPage { Id = Guid.NewGuid(), ActionName = "ActionName1", ControllerName = "ControllerName1", MenuItemId = menuItem1Id };
    var contentPage2 = new ContentPage { Id = Guid.NewGuid(), ActionName = "ActionName2", ControllerName = "ControllerName2", MenuItemId = menuItem2Id };
    var contentPage3 = new ContentPage { Id = Guid.NewGuid(), ActionName = "ActionName3", ControllerName = "ControllerName3", MenuItemId = menuItem3Id };

    var menuItem1 = new MenuItem { Id = menuItem1Id, MenuId = MenuId, DisplayName = "MenuItem1", ExternalUrl = null, Target = Target._self, Active = true, ContentPage = contentPage1 };
    var menuItem2 = new MenuItem { Id = menuItem1Id, MenuId = MenuId, DisplayName = "MenuItem2", ExternalUrl = null, Target = Target._self, Active = true, ContentPage = contentPage2 };
    var menuItem3 = new MenuItem { Id = menuItem1Id, MenuId = MenuId, DisplayName = "MenuItem3", ExternalUrl = null, Target = Target._self, Active = true, ContentPage = contentPage3 };

    var menu = new Models.Menu { Id = MenuId, Name = "TestMenu", SiteId = Guid.NewGuid(), MenuItems = new List<MenuItem> { menuItem1, menuItem2, menuItem3 } };

    Context.Menus.Add(menu);
    Context.MenuItems.Add(menuItem1);
    Context.MenuItems.Add(menuItem2);
    Context.MenuItems.Add(menuItem3);

    var menuItemQueryResult = new List<MenuItemQueryResult>
    {
        new MenuItemQueryResult { Id = menuItem1Id, DisplayName = "MenuItem1", ExternalUrl = null, Target = Target._self, Active = true, ActionName = "ActionName1", ControllerName = "ControllerName1" },
        new MenuItemQueryResult { Id = menuItem1Id, DisplayName = "MenuItem2", ExternalUrl = null, Target = Target._self, Active = true, ActionName = "ActionName2", ControllerName = "ControllerName2" },
        new MenuItemQueryResult { Id = menuItem1Id, DisplayName = "MenuItem3", ExternalUrl = null, Target = Target._self, Active = true, ActionName = "ActionName3", ControllerName = "ControllerName3" }
    };

    Mapper = new Mock<IMappingEngine>();

    Mapper.Setup(m => m.Map<IEnumerable<MenuItem>, IEnumerable<MenuItemQueryResult>>(It.IsAny<IEnumerable<MenuItem>>()))
                        .Returns(menuItemQueryResult);
}

[TestMethod]
public void Retrieve_RequestMenu_QueryResultReturned()
{
    var handler = new MenuQueryHandler(Context, Mapper.Object);
    var query = new MenuQuery("TestMenu");
    var result = handler.Retrieve(query);
    Assert.IsNotNull(result);
    Assert.IsInstanceOfType(result, typeof(MenuQueryResult));

    var item = result.FirstOrDefault(r => r.Id == menuItem1Id);
    Assert.IsNotNull(item);
}

这是我正在测试的内容:

public class MenuQueryHandler : IQueryHandler<MenuQuery, MenuQueryResult>
{
    private IDbContext Context { get; set; }
    private IMappingEngine Mapper { get; set; }

    public MenuQueryHandler(IDbContext context, IMappingEngine mapper)
    {
        Context = context;
        Mapper = mapper;
    }

    public MenuQueryResult Retrieve(MenuQuery query)
    {
        Ensure.Argument.Is(query != null);
        Ensure.Argument.IsNot(query.MenuName == string.Empty);

        // Create the view model query result
        var result = new List<MenuItemQueryResult>();

        // Pull the required item from the cont.ext
        var menuItems = Context.MenuItems.Include(m => m.ContentPage).ToList();

        Mapper.Map(menuItems, result);

        return new MenuQueryResult(result);

    }
}

测试运行,但映射从未发生。

您的模拟映射器设置为 return menuItemQueryResult,但在您的实现中您没有获取 Mapper.Map 函数的结果。在我看来,你应该像这样使用你的映射器:

result = Mapper.Map(menuItems);

编辑:

如果您使用的是 Automapper 并且它已针对以下类型正确配置:

result = Mapper.Map<IEnumerable<MenuItem>, List<MenuItemQueryResult>>(menuItems);

有几个问题:

  1. 您并不是在模拟您实际调用的方法。您正在测试的方法调用此方法:

    TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
    

    Map 的这种重载采用现有的 destination 对象并将其映射到其中。

    在您的测试中,您正在模拟 Map 的重载 returns new TDestination:[=32 的实例=]

    TDestination Map<TSource, TDestination(TSource source);
    

    请注意,您模拟的那个需要一个参数,而您实际调用的那个需要两个。

  2. 您的 Setup 方法在 IEnumerable<MenuItem>IEnumerable<MenuItemQueryResult> 之间建立了一个假映射。在您的测试中,您实际上是在用 List<MenuItem>List<MenuItemQueryResult>.

    调用 Map

    在实际使用中,AutoMapper会为您使用IEnumerable映射处理ListList的映射。但是,使用模拟方法时,您实际上并没有使用您指定的 exact 类型参数调用该方法。因此,您必须更改 Setup 调用并更改假映射。

因此,要解决这些问题,您可以执行以下两项操作之一:

  1. 更改方法以使用 Map 的重载,即 returns 一个新实例。

    看起来你不需要使用带有两个参数的重载,所以你可以调整方法:

    var result = Mapper.Map<List<MenuItemQueryResult>(menuItems);
    

    然后在你的测试中:

    Mapper.Setup(m => m.Map<List<MenuItem>, List<MenuItemQueryResult>>(It.IsAny<List<MenuItem>>())
    
    Mapper
        .Setup(m => m.Map<List<MenuItem>, List<MenuItemQueryResult>>(It.IsAny<List<MenuItem>>()))
        .Returns(menuItemQueryResult);
    
  2. 更改测试以模拟 Map 的正确重载。

    这有点不直观,但有可能。您必须提供 Map 方法的伪实现:

    Mapper
        .Setup(m => m.Map<List<MenuItem>, List<MenuItemQueryResult>>(It.IsAny<List<MenuItem>>(), It.IsAny<List<MenuItem>>()))
        .Callback((List<MenuItem> menuItems, List<MenuItemQueryResult> queryResults) =>
        {
            queryResults.AddRange(menuItemQueryResult);
        });