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模拟列表转换