Automatic unit testing

I am having trouble getting the auto mapper to work in my unit test. I am implementing a mapping engine and this works great in code but not in test. Here is my test and setup. 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 match never happens.

+3


source to share


2 answers


There are several problems:

  • You are not mocking the method you actually call it. The method you are testing calls this method:

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

    This overload Map

    takes an existing object destination

    and maps to it.

    In your test, you mock an overload Map

    that returns a new instance TDestination

    :

    TDestination Map<TSource, TDestination(TSource source);
    
          

    Note that the one you are kidding takes one parameter, and the one you actually call takes two.

  • Your method Setup

    makes a fake match between IEnumerable<MenuItem>

    and IEnumerable<MenuItemQueryResult>

    . In your test, you are actually calling Map

    with List<MenuItem>

    and List<MenuItemQueryResult>

    .

    In real use, AutoMapper will handle the mapping List

    to List

    for your use by mapping IEnumerable

    . However, if you mock a method, you are not actually calling the method with the exact parameters you specified. Therefore, you will have to change the call Setup

    and change the fake mapping.



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

  • Change the overload usage method Map

    that returns a new instance.

    It doesn't look like you need to use an overload that takes two parameters, so you can customize 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);
    
          

  • Modify the test to poke fun at correct overload Map

    .

    It's a little less intuitive, but possible. You will need to provide a fake method implementation 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);
        });
    
          

+5


source


Your mock mapper is set up to return a menuItemQueryResult, but you don't take the result of the Mapper.Map function when you implement it. In my opinion you should use your cartographer like this:

result = Mapper.Map(menuItems);

      

Edit:



If you are using Automapper and configured correctly for types:

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

      

0


source







All Articles