Lucia
Lucia

Reputation: 21

Unit test Automapper Resolver

I am using .net with xUnit and NSubstitute. I want to unit test mapping from UnitPostVModel to UnitBLLModel. When using UnitImgUrlResolver I get an error where it says that Cannot dynamically create an instance of type 'APMAPI.ServicesBLL.Resolvers.UnitImgUrlResolver'. Reason: No parameterless constructor defined.

 public class AutoMapperProfile : Profile
    {
        
        public AutoMapperProfile()
        {
                CreateMap<UnitPostVModel, UnitBLLModel> ()
                .ForMember(dest => dest.imgUrl, opt => opt.MapFrom<UnitImgUrlResolver>());
        }
    }

This is my Resolver:

public class UnitImgUrlResolver : IValueResolver<UnitPostVModel, UnitBLLModel, string>
    {
        IAzureBlobStorageService _azureBlobStorageService;
        public UnitImgUrlResolver(IAzureBlobStorageService azureBlobStorageService)
        {
            _azureBlobStorageService = azureBlobStorageService;
        }

        public string Resolve(
            UnitPostVModel src,
            UnitBLLModel destination,
            string destMember,
            ResolutionContext context)
        {
            if(src.image == null)
                return string.Empty;
            var imageName = GenerateRandomAlphaNumeric(10);
            var url = _azureBlobStorageService.UploadImageToBlobStorage(src.image, imageName);

            return url;
        }

    }

This is my test function:


    public class AutoMapperTests
    {
        private readonly IMapper _mapper;
        public AutoMapperTests()
        {
            var config = new MapperConfiguration(cfg => {
                cfg.AddProfile<AutoMapperProfile>();
            });
            _mapper = config.CreateMapper();
        }

        [Fact]
        public void Should_Map_UnitPostVModel_To_UnitBLLModel()
        {
            // Arrange
            var unitPostVModel = new UnitPostVModel
            {
               abbreviation = "A",
               name = "Test"
            };
            
            // Act
            var result = _mapper.Map<UnitBLLModel>(unitPostVModel);


            // Assert
            Assert.NotNull(result);
            Assert.Equal("MockedImageUrl", result.imgUrl);
        }
        #endregion


    }

But I get this error:

Message: 
    AutoMapper.AutoMapperMappingException : Error mapping types.
    
    Mapping types:
    UnitPostVModel -> UnitBLLModel
    APMAPI.Models.VModels.UnitPostVModel -> APMAPI.Models.BLLModels.UnitBLLModel
    
    Type Map configuration:
    UnitPostVModel -> UnitBLLModel
    APMAPI.Models.VModels.UnitPostVModel -> APMAPI.Models.BLLModels.UnitBLLModel
    
    Destination Member:
    imgUrl
    
    ---- System.MissingMethodException : Cannot dynamically create an instance of type 'APMAPI.ServicesBLL.Resolvers.UnitImgUrlResolver'. Reason: No parameterless constructor defined.

I've tried creating a mock for IAzureBlobStorageService

    private readonly IMapper _mapper;
    private readonly IAzureBlobStorageService _azureBlobStorageService;

    public AutoMapperTests()
    {
        // Create a mock for IAzureBlobStorageService
        _azureBlobStorageService = Substitute.For<IAzureBlobStorageService>();

        // Create an instance of UnitImgUrlResolver with the mock
        var unitImgUrlResolver = new UnitImgUrlResolver(_azureBlobStorageService);

        var config = new MapperConfiguration(cfg => {
            // Register the resolver instance
            cfg.CreateMap<UnitPostVModel, UnitBLLModel>()
                .ForMember(dest => dest.imgUrl, opt => opt.MapFrom(src => unitImgUrlResolver.Resolve(src, null, null, null)));
        });

        _mapper = config.CreateMapper();
    }
    [Fact]
    public void Should_Map_UnitPostVModel_To_UnitBLLModel()
    {
        // Arrange
        var unitPostVModel = new UnitPostVModel
        {
            abbreviation = "A",
            name = "Test",
            description = "Test",
            propertyId = Guid.NewGuid(),
            type = Guid.NewGuid(),
            outOfService = false
        };

        // Mock the method call to UploadImageToBlobStorage
        _azureBlobStorageService.UploadImageToBlobStorage(Arg.Any<IFormFile>(), Arg.Any<string>())
            .Returns("MockedImageUrl");

        // Act
        var result = _mapper.Map<UnitBLLModel>(unitPostVModel);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("MockedImageUrl", result.imgUrl);
    }
}

But I still get the same error.

Upvotes: 0

Views: 108

Answers (1)

julealgon
julealgon

Reputation: 8181

If you are not resolving the mapper from a DI container, you should be able to leverage the custom extension point called ConstructServicesUsing to override how AutoMapper will instantiate your resolver. This should allow you to mock its implementation:

private readonly IMapper _mapper;
public AutoMapperTests()
{
    var config = new MapperConfiguration(cfg => {
        cfg.ConstructServicesUsing(type => 
            Substitute.For([type], [Substitute.For<IAzureBlobStorageService>()]));
        cfg.AddProfile<AutoMapperProfile>();
    });
    _mapper = config.CreateMapper();
}

With this, we tell AutoMapper to call Substitute.For([type], [Substitute.For<IAzureBlobStorageService>()]) whenever it encounters a custom resolver or converter instance. This should then leverage NSubstitute to create a mock for that implementation.

Of course, this simple implementation assumes your only custom implementation is that UnitImgUrlResolver class, but if you have other resolvers and converters being used in the same profile you'd have to change the factory code accordingly to handle each type specifically.

More information on this mechanism here:

Upvotes: 0

Related Questions