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?