Unit testing automapper

18,079

Solution 1

There are a few issues:

  1. 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 existing destination object and maps into it.

    In your test, you're mocking the overload of Map that returns a new instance of TDestination:

    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.

  2. Your Setup method sets up a fake mapping between IEnumerable<MenuItem> and IEnumerable<MenuItemQueryResult>. In your test, you're actually calling Map with a List<MenuItem> and a List<MenuItemQueryResult>.

    In actual usage, AutoMapper is going to handle the List to List mapping for your using the IEnumerable 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 the Setup call and change the fake mapping.

So to get around those problems you can do one of two things:

  1. 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);
    
  2. 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);
Share:
18,079
Ethan Schofer
Author by

Ethan Schofer

Updated on June 04, 2022

Comments

  • Ethan Schofer
    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.