Unit testing automapper
Solution 1
There are a few issues:
You aren't mocking the method you're actually calling. The method you're testing calls this method:
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
This overload of
Map
takes an existingdestination
object and maps into it.In your test, you're mocking the overload of
Map
that returns a new instance ofTDestination
:TDestination Map<TSource, TDestination(TSource source);
Note that the one you're mocking takes one parameter and the one you're actually calling takes two.
Your
Setup
method sets up a fake mapping betweenIEnumerable<MenuItem>
andIEnumerable<MenuItemQueryResult>
. In your test, you're actually callingMap
with aList<MenuItem>
and aList<MenuItemQueryResult>
.In actual usage, AutoMapper is going to handle the
List
toList
mapping for your using theIEnumerable
mapping. With a mocked method though, you're not actually calling the method with the exact type parameters you specified. So you'll have to change theSetup
call and change the fake mapping.
So to get around those problems you can do one of two things:
Change the method to use the overload of
Map
that returns a new instance.It doesn't look like you need to use the overload that takes two parameters, so you could tweak the method:
var result = Mapper.Map<List<MenuItemQueryResult>(menuItems);
And then in your test:
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);
Change the test to mock the correct overload of
Map
.This is a little less intuitive, but possible. You'll have to provide a fake implementation of the
Map
method: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); });
Solution 2
Your mock mapper is set up to return menuItemQueryResult but in you implementation you don't take the result of the Mapper.Map function. In my opinion you should use your mapper like this:
result = Mapper.Map(menuItems);
Edit:
In case you are using Automapper and it is correctly configured for the types:
result = Mapper.Map<IEnumerable<MenuItem>, List<MenuItemQueryResult>>(menuItems);
Ethan Schofer
Updated on June 04, 2022Comments
-
Ethan Schofer almost 2 years
I am having trouble getting automapper to work in my unit test. I am injecting the mapping engine, and this works fine in code, but not in the test. here is my test set up and test. I am using Moq to mock the mapping engine.
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); }
And here is what I am testing:
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); } }
The test runs, but the mapping never occurs.