0

How do I test an Automapper ITypeConverter Converter with a ResolutionContext?

I have a complicated converter and want to test it explicitly. It needs an argument ResolutionContext which I cannot get to, nor create nor mock. Is it possible?

public class MyConverter : ITypeConverter<SourceType, TargetType>
{
    public TargetType Convert(SourceType source, TargetType destination, ResolutionContext context)
    {
       ...complicated code...
    }
}

Edit/Clarification: I am trying to not call myMapper.Map(... but these only the ´MyConverter.Convert` function.
I know it can be considered the wrong-way-to-test as one should only test public methods and this class/method is only public due to technical reasons and should really be private from OO point of view. But that discussion is for another forum.

LosManos
  • 7,195
  • 6
  • 56
  • 107
  • 1
    You don't write tests for AutoMapper components. That's the job of AutoMapper itself. Just create a mapper from your configuration, create a source object with some specific values, map on the desired destination object and check the properties of the destination instance for correctness. That's it. – Oliver Jun 16 '23 at 19:52
  • Are you needing/using the context in your converter? If so you probably can't test the Convert method directly. And I agree that TDD principles enforce that every method be testable. It's bad behavior for a modern framework to not allow mocking of framework components. In my case, I wasn't using context, so I changed my method signature to have a nullable ResolutionContext?. The framework will always pass a context, but I can pass null in my test cases. – Jim K Jun 30 '23 at 23:13

1 Answers1

2

The source code of the TypeConverter unit tests of AutoMapper itself show that they follow the regular flow:

  • Set up a MapperConfiguration.
  • Create a Mapper from it.
  • Call one of the Map overloads.

By doing so AutoMapper takes care of passing a ResolutionContext to the Convert method.

Below code shows how the Convert method receives a ResolutionContext with a preconfigured Items dictionary.

var configuration = new MapperConfiguration(
    o => o.CreateMap<SourceType, TargetType>().ConvertUsing<MyConverter>()
    // More mappings go here.
    );

var mapper = configuration.CreateMapper();

var source = new SourceType 
{
    Name = "foo" 
};

var target = mapper.Map<TargetType>(
    source,
    o =>
    {
        o.Items["foo"] = "bar";
    });

Console.WriteLine(target.Name); // bar
public class SourceType
{
    public string Name { get; set; }
}

public class TargetType
{ 
    public string Name { get; set; }
}

public class MyConverter : ITypeConverter<SourceType, TargetType>
{
    public TargetType Convert(SourceType source, TargetType destination, ResolutionContext context)
    {
        // Complicated code goes here.
    
        return new TargetType
        {
            Name = (string)context.Items[source.Name]
        };
    }
}
pfx
  • 20,323
  • 43
  • 37
  • 57
  • 1
    That solution is how I solved the problem, to call `mapper.Map...`. My question was about how to not test the mapper but only the very `MyConverter.Concert`method. Thx anyway. I have updated the question. – LosManos Jun 17 '23 at 09:49
  • 1
    As long as [ResolutionContext](https://github.com/AutoMapper/AutoMapper/blob/104a192407858fe3a54a8a937ca95a34fd7c6959/src/AutoMapper/ResolutionContext.cs#L12) has a non public constructor or abstraction, there isn't much other to try when a converter relies on that `ResultionContext`. – pfx Jun 17 '23 at 10:21
  • I also believe in @pfx and @Oscar 's answer and comment. The "correct" solution is probably to test the very `mapper.Map`. If the tests get hard to read - create helper functions to create in data in a readable way. – LosManos Jun 18 '23 at 13:28
  • 1
    I cannot mark you answer as the Answer though - as it doesn't Answer the Question. An upvote is all. – LosManos Jun 18 '23 at 13:30
  • @LosManos, That is totally fine. I'm also open/curious for an other/better approach. – pfx Jun 18 '23 at 13:48