单元测试自动映射器
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);
有几个问题:
您并不是在模拟您实际调用的方法。您正在测试的方法调用此方法:
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
Map
的这种重载采用现有的 destination
对象并将其映射到其中。
在您的测试中,您正在模拟 Map
的重载 returns new TDestination
:[=32 的实例=]
TDestination Map<TSource, TDestination(TSource source);
请注意,您模拟的那个需要一个参数,而您实际调用的那个需要两个。
您的 Setup
方法在 IEnumerable<MenuItem>
和 IEnumerable<MenuItemQueryResult>
之间建立了一个假映射。在您的测试中,您实际上是在用 List<MenuItem>
和 List<MenuItemQueryResult>
.
调用 Map
在实际使用中,AutoMapper会为您使用IEnumerable
映射处理List
到List
的映射。但是,使用模拟方法时,您实际上并没有使用您指定的 exact 类型参数调用该方法。因此,您必须更改 Setup
调用并更改假映射。
因此,要解决这些问题,您可以执行以下两项操作之一:
更改方法以使用 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);
更改测试以模拟 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);
});
我无法让自动映射器在我的单元测试中工作。我正在注入映射引擎,这在代码中运行良好,但在测试中却不行。这是我的测试设置和测试。我正在使用 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);
有几个问题:
您并不是在模拟您实际调用的方法。您正在测试的方法调用此方法:
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
Map
的这种重载采用现有的destination
对象并将其映射到其中。在您的测试中,您正在模拟
Map
的重载 returns newTDestination
:[=32 的实例=]TDestination Map<TSource, TDestination(TSource source);
请注意,您模拟的那个需要一个参数,而您实际调用的那个需要两个。
您的
调用Setup
方法在IEnumerable<MenuItem>
和IEnumerable<MenuItemQueryResult>
之间建立了一个假映射。在您的测试中,您实际上是在用List<MenuItem>
和List<MenuItemQueryResult>
.Map
在实际使用中,AutoMapper会为您使用
IEnumerable
映射处理List
到List
的映射。但是,使用模拟方法时,您实际上并没有使用您指定的 exact 类型参数调用该方法。因此,您必须更改Setup
调用并更改假映射。
因此,要解决这些问题,您可以执行以下两项操作之一:
更改方法以使用
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);
更改测试以模拟
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); });