How to mock custom ValueResolver constructor parameters

36 views
Skip to first unread message

denis....@gmail.com

unread,
Sep 1, 2019, 10:07:40 AM9/1/19
to AutoMapper-users
I have a project in .NET Core and I'm using AutoMapper to map between my types. But I got into a problem, where I would like to mock the parameters of my custom value resolvers. I created dummy example to show the problem:

Let's say, that I have a ValueResolver, that needs to have access to DB to resolve the given member:

public class NumberProfile : Profile
{
    public NumberProfile()
    {
        CreateMap<NumberModel, NumberDto>()
            .ForMember(dest => dest.Number, opt => opt.MapFrom<NumberValueResolver>());
    }
}

public class NumberValueResolver : IValueResolver<NumberModel, NumberDto, int>
{
    private readonly IRepository _repository;

    public NumberValueResolver(IRepository repository)
    {
        _repository = repository;
    }

    public int Resolve(NumberModel source, NumberDto destination, int destMember, ResolutionContext context)
    {
        return _repository.GetNumber().Number + 3;
    }
}

Let's imagine, there is logic in Resolve method, that needs to be tested and I want to mock the repository to able to test it.
This is what I tried in my tests

var repositoryMock = new Mock<IRepository>();
repositoryMock.Setup(s => s.GetNumber()).Returns(new NumberModel { Number = 3 });

var mapper = new MapperConfiguration(mc =>
{
    mc.AddProfile<NumberProfile>();
    mc.ConstructServicesUsing(s => new NumberValueResolver(repositoryMock.Object));
});

var service = new Service(repositoryMock.Object, mapper.CreateMapper());

And then I call a method on service that triggers the mapping.
This case is good, it works and the test passes. The problem comes when I add another ValueResolver.

public class NumberProfile : Profile
{
    public NumberProfile()
    {
        CreateMap<NumberModel, NumberDto>()
            .ForMember(dest => dest.Number, opt => opt.MapFrom<NumberValueResolver>())
            .ForMember(dest => dest.AnotherNumber, opt => opt.MapFrom<AnotherNumberValueResolver>());
    }
}

public class NumberValueResolver : IValueResolver<NumberModel, NumberDto, int>
{
    private readonly IRepository _repository;

    public NumberValueResolver(IRepository repository)
    {
        _repository = repository;
    }

    public int Resolve(NumberModel source, NumberDto destination, int destMember, ResolutionContext context)
    {
        return _repository.GetNumber().Number + 3;
    }
}

public class AnotherNumberValueResolver : IValueResolver<NumberModel, NumberDto, string>
{
    public int Resolve(NumberModel source, NumberDto destination, string destMember, ResolutionContext context)
    {
        return $"{source.Number} number(s)";
    }
}

And then I thought I would just do this in my tests

var repositoryMock = new Mock<IRepository>();
repositoryMock.Setup(s => s.GetNumber()).Returns(new NumberModel { Number = 3 });

var mapper = new MapperConfiguration(mc =>
{
    mc.AddProfile<NumberProfile>();
    mc.ConstructServicesUsing(s => new NumberValueResolver(repositoryMock.Object));
    mc.ConstructServicesUsing(s => new AnotherNumberValueResolver());
});

var service = new Service(repositoryMock.Object, mapper.CreateMapper());

But then I get error:
System.InvalidCastException : Unable to cast object of type 'AutoMapper_app.ValueResolvers.AnotherNumberValueResolver' to type 'AutoMapper_app.ValueResolvers.NumberValueResolver'.

Did I misunderstand usage of ConstructServicesUsing or ValueResolvers or is it something completely different? Am I missing something super obvious? Is it maybe bad design?
Reply all
Reply to author
Forward
0 new messages