How to Mock a list transformation using AutoMapper

I am using AutoMapper and have a definition of mapping engine as

private readonly IMappingEngine _mappingEngine;

I initialize it via constructor injection and use in the code as below

var product=//Get a single product
var productModel = _mappingEngine.Map<ProductModel>(product);

The above works perfectly. I now need to map a list of Product to list of ProductModel The following works in the controller action

var entities =//Get list of products
var model = entities.Select(e => _mappingEngine.Map<ProductModel>(e));

The above LINQ code uses foreach and converts each Product to a ProductModel Now I need to unit test the above code but unable to use Moq to mock the LINQ statement above

I've tried the following

var mapper = new Mock<IMappingEngine>();
mapper.Setup(m => m.Map<ProductModel>(product)).Returns(ProductModel);

The above mapper setup works for single object mapping. How can we setup the above using a list of products

So, I want to be able to setup a list of Product like this:

var productList=new List<Product>{new Product{Id=1,name="product 1"},
                                  new Product{Id=2,name="product 2"}};

and define a mocking that will return a list of ProductModel like this:

var productModelList=new List<ProductModel>{new ProductModel{Id=1,name="product 1"},
                                            new ProductModel{Id=2,name="product 2"}};  

when my test calls the controller (which uses the mock IMappingEngine to transform the list)

var model = entities.Select(e => _mappingEngine.Map<ProductModel>(e));

So, when writing Moq unit tests how can we setup the above so that _mappingEngine.Map that takes productList as input and returns productModelList ?


First up, do you really need to mock the mapping from Product to ProductModel , or would it be just as valid to create an instance of IMappingEngine and supply it to your controller, rather than a Mock.Object .

Something as simple adding the mapping in your test setup:

Mapper.CreateMap<Product, ProductModel>();

and clearing the mapping in your test teardown:

Mapper.Reset();

Would allow you to just supply Mapper.Engine to controllers constructor in your test. Obviously it depends on your approach to testing, but for something like AutoMapper which is well used and reliable, with little time overhead, this may well be a valid approach.

Assuming you do want to mock the mapping, you can use a callback to return a different item for each call, doing something like this:

// Create a list of the mapped values you're expecting
var productModels = new List<ProductModel> {
    new ProductModel { Id=11,name="eleven"},
    new ProductModel { Id=12,name="twelve"},
    new ProductModel { Id=13,name="thirteen"}
};

// Mock the IMappingEngine
var engine = new Mock<IMappingEngine>();

// Create a variable to count the calls
var calls=0;

// Mock ALL calls to map, where the destination is ProductModel
// and source is Product
engine.Setup(m => m.Map<ProductModel>(It.IsAny<Product>()))
      .Returns(()=>productModels[calls]) // Return next productModel
      .Callback(()=>calls++)   // Increment counter to point to next model

You should notice, that the mock Setup is mocking individual mappings, not mapping a list of Product to a list of ProductModel . That's because your call to entities.Select(e => _mappingEngine.Map<ProductModel>(e)) is looping through your list an item at a time, not asking the mapping engine (or your mock) to map the list in one go...

If you need to be more precise in your mocking, then it's also possible to extend the Callback to verify that the expected Product is being mapped. You probably won't want to do this every time, but it can be useful in some circumstances. So, you could do something like:

// Declare a list of expected products to map from
var products = new List<Product> { 
    new Product {Id=1, name="One"},
    new Product {Id=2, name="Two"},
    new Product {Id=3, name="Three"}
};


engine.Setup(m => m.Map<ProductModel>(It.IsAny<Product>()))
      .Returns(() => productModels[calls])
      .Callback<Product>(x => {Assert.AreEqual(x.Id, products[calls].Id); calls++;});
链接地址: http://www.djcxy.com/p/74400.html

上一篇: 使用NInject注入自动缩放器映射器

下一篇: 如何使用AutoMapper模拟列表转换