Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Mock an AutoMapper IMapper object in Web API Tests With StructureMap Dependency Injection?

So I've build a WebAPI from scratch, including some best practices that I've found online such as Dependency Injection and Domain<->DTO mapping using auto mapper etc.

My API Controllers now look similar to this

public MyController(IMapper mapper)
{
}

and AutoMapper Registry:

public AutoMapperRegistry()
{
    var profiles = from t in typeof(AutoMapperRegistry).Assembly.GetTypes()
                   where typeof(Profile).IsAssignableFrom(t)
                   select (Profile)Activator.CreateInstance(t);

    var config = new MapperConfiguration(cfg =>
    {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
    });

    For<MapperConfiguration>().Use(config);
    For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
}

I'm also building a few test cases, implementing MOQ, and this is where i feel a little unsure. whenever calling my controllers, I need to pass in an IMapper like this:

var mockMapper = new Mock<IMapper>();
var controller = new MyController(mockMapper.Object);

But then, how do i configure the IMapper to have the correct mappings? It feels redundant to recreate the same logic I've already created before to configure the Mapper. so I am wondering what is the recommended approach to do this?

like image 782
kob490 Avatar asked Mar 18 '16 00:03

kob490


2 Answers

That's pretty simple: if you mock IMapper and imagine it as a fully abstract concept of mapping data from one object to another, then you have to treat is an abstraction and not imply there's a real automapper behind it.

First you should not register any existing profile at all, you should instead setup IMapper.Map method to return specific object when given another object.

So for each profile used for specific method you have to do a setup, looking approximately like this:

var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DestinationClass>(It.IsAny<SourceClass>()))
    .Returns((SourceClass source) =>
        {
            // abstract mapping function code here, return instance of DestinationClass
        });

In this case, your test knows nothing about actual IMapper implementation - it just uses it methods to get the data you expect from actual IMapper implementation to receive.

like image 153
Red Avatar answered Sep 23 '22 22:09

Red


This might me another solution

        //auto mapper configuration
        var mockMapper = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new AutoMapperProfile()); //your automapperprofile 
        });
        var mapper = mockMapper.CreateMapper();

And then call then controller like so

var controller = new YourController(imapper:mapper,..otherobjects..);

This way it will serve the purpose or else if you create mock object for IMapper then it will return what you ask it to return.

like image 38
Iftikhar Ali Ansari Avatar answered Sep 22 '22 22:09

Iftikhar Ali Ansari