0

I'm refreshing my knowledge about Automapper API.

Exploring IMapper interface, I found several posts about dependency injection and Automapper, e.g. this and this (they are about .NET Core, but this doesn't matter in context of the question).

Assuming, that typical use of mappers like Automapper is:

public class Person
{
    // person properties here
}

public class PersonDto
{
    // person DTO properties here
}

public class SomeApi
{
    // other code here

    public PersonDto FindByName(string name)
    {
        var person = dbContext.People.FirstOrDefault(_ => _.Name == name);

        // mapper is IMapper
        return mapper.Map<PersonDto>(person);
    }
}

why we may want to inject and/or mock IMapper?

I understand, why injecting things like dbContext could be useful - when writing a unit test, you have to setup test environment somehow to make testing code isolated from outer world.

But what's the point for mapper?

I mean, mapping is a part of method's logic. It's not an "outer world". This is what method was written for, and this is what must be tested, but not mocked.

If I rewrite FindByName without using Automapper, the code will look like this:

    public PersonDto FindByName(string name)
    {
        var person = dbContext.People.FirstOrDefault(_ => _.Name == name);

        if (person == null)
            return null;

        return new PersonDto
        {
            Name = person.Name,

            // etc
        };
    }

So, what do we injecting/mocking here? Assignment operators? Seriously?

It looks like dependency injection for dependency injection without any benefit.

Am I missing something? Maybe, there are cases, when injecting/mocking Automapper is really useful (I'm interested in cases found in practice, not in theoretic ones)?

Community
  • 1
  • 1
Dennis
  • 37,026
  • 10
  • 82
  • 150
  • I agree. I have the same view: mapping is internal details of the method, and as the client I don't need to care whether the method is doing manual mapping or using AutoMapper. In this case I consider IMapper to be over-abstraction. – Phuong Nguyen Feb 14 '17 at 07:01

2 Answers2

3

Unpacking this into two questions:

  • Why would you inject IMapper?
  • Why would you mock IMapper?

For the first question, I would inject IMapper in case one of the mapping extensions needs a dependency. For example, if you're using a custom IValueResolver or ITypeConverter that uses a DbContext, you want to make sure that the right DbContext is supplied. If you set up your container to create a Mapper instance with a factory method, then the extensions use that factory method callback to your container.

If you use the static Mapper instance, it's bit more difficult to make sure your value resolvers/type converters get their dependencies from a container.

For the second question, I wouldn't ever mock IMapper in any kind of test. It's like mocking JSON.net, or StringBuilder, not much point.

Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • Thanks a lot, Jimmy. It's much more clear now. So, there's exactly one case: it's when you need to extend Automapper's config somehow, using `IValueResolver`s or `ITypeConverter`. If you don't need it, it's OK to create mapper instances (or to use static API) directly. Right? – Dennis Feb 16 '17 at 05:40
0

Well it would be for the same reason: To do a pure unit test. Let's take the code you have in your question and modify it and add a simple constructor as example:

public class SomeApi
{
    private IMapper mapper;
    public SomeApi(IMapper mapper)
    {
        this.mapper = mapper;
    }
    // other code here

    public PersonDto FindByName(string name)
    {
        var person = dbContext.People.FirstOrDefault(_ => _.Name == name);

        // mapper is IMapper
        return mapper.Map<PersonDto>(person);
    }
}

What if I wanted to write a unit test for FindByName and I wanted to mock the IMapper because I want the test to be purely based on my mock and not AutoMapper. Or what if I had not decided that I will use AutoMapper but any type which implements IMapper. Or maybe I do not want my SomeApi class to be coupled to, or have any dependency on AutoMapper.

If the SomeApi class is within an assembly, it does not need to know about AutoMapper and all it should know about is the IMapper. What implementation of IMapper could be decided at the Composition Root. This way if the people who are using your API, do not like AutoMapper, for whatever reason, can use any mapper they want.

In the comments to this answer you stated:

1) What are you testing, when you throwing away the biggest part of method?

I am testing to see if the code which is looking in the database for the person by name is working.

2) Injecting IMapper, you bringing dependency from Automapper.

No I am not. If your SomeApi class is not coupled to AutoMapper's IMapper, but just another interface which is called IMapper but with less or more methods or the same methods, then that is what you are coupling to.

3) Did you ever see custom IMapper implementations (I mean, in practice)?

No, I have not. I have not seen a compiler either but they do exist. I had seen a while ago someone wrote an article about AutoMapper's (and other mapper's) performance vs manual mapping and I can see why some people may want to use their own mapper. Please see this.

Your question was about why this is done, and those are the reasons I can think of. Am I stating that you should be doing that? Absolutely not.

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • Just some points. 1) Mapping from entity to DTO is a part of method's logic. In fact, there's nothing else to test here. This is what method exists for (see sample with by-hand mapping). What are you testing, when you throwing away the biggest part of method? 2) Injecting `IMapper`, you bringing dependency from Automapper. If you really need to bring custom mapping, you need to invent your `IMapper`, and wrap Automapper's one inside it. 3) Did you ever see custom `IMapper` implementations (I mean, in practice)? – Dennis Feb 14 '17 at 06:07