Use ResolutionContext in tests for Automapper

713 views Asked by At

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.

2

There are 2 answers

5
pfx On

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]
        };
    }
}
0
Yash Dhanwantri On

In case your custom converter has any dependencies, this can be a possible workaround:

As per Automapper, ResolutionContext has no public constructor so mocking this is not possible, neither instantiating. We'll have to use an actual Mapper in order to test the custom converters with dependencies.

Here is the sample code for this, hope this helps:

public class MyConverterTests
{
    #region Private Variables
    private static readonly MyCustomConverter _myCustomConverter;
    private static readonly Mock<DependencyA> _dependencyA;
    private readonly IMapper _mapper;
    #endregion

    static MyConverterTests()
    {
        _dependencyA = new Mock<DependencyA>();
        _myCustomConverter = new MyCustomConverter(_dependencyA.Object);
    }

    public MyConverterTests()
    {
        var mapperConfiguration = new MapperConfiguration(cfg => cfg.AddProfile<CustomMapperProfile>());
        _mapper = new AutoMapper.Mapper(mapperConfiguration);
    }

    [Fact]
    public void TestCaseA()
    {
        // Do Testing
    }

    public class CustomMapperProfile : Profile
    {
        public CustomMapperProfile()
        {
            CreateMap<Source, Destination>()
                .ConvertUsing(_myCustomConverter);
        }
    }
}

public class MyCustomConverter : ITypeConverter<Source, Destination>
{
    public MyCustomConverter(DependencyA dependency)
    {

    }

    public Destination Convert(Source source, Destination destination, ResolutionContext context)
    {
        throw new System.NotImplementedException();
    }
}

public class DependencyA
{

}

public class Destination
{

}

public class Source
{

}