qtran-dev
qtran-dev

Reputation: 31

Mock Repository With Expected Object Using ReturnsAsync Returns Null

I'm creating a random object that I'm expecting back from the test, and using the ReturnsAsync method when setting up the mock repo. The controller inside the Unit Test is returning null, however.

The main difference between my code and a lot of the others is that I'm using AutoMapper to return the DTO object in my controller as an OK object. I'm not sure if that's causing my issue.

Controller Instantiation

private readonly ISuperHeroRepo _repo;
public IMapper _mapper { get; }

public SuperHeroController(ISuperHeroRepo repo, IMapper mapper)
{
    _repo = repo;
    _mapper = mapper;
}

Repo Method

public async Task<SuperHero> GetSuperHero(int id)
{
    return await _db.SuperHeros.FindAsync(id);
}

Mapper Profile

public class SuperHeroProfiles : Profile
{
    public SuperHeroProfiles()
    {
        //Source -> Target
        CreateMap<SuperHero, SuperHeroReadDTO>()
            .ForMember(target => target.LegalName, option => option.MapFrom(source => $"{source.LegalFirstName} {source.LegalLastName}"))
            .ForMember(target => target.Jurisdiction, option => option.MapFrom(source => $"{source.JurisdictionCity}, {source.JurisdictionState}"));

        CreateMap<SuperHero, SuperHeroDTO>();
    }
}

Controller Method

[HttpGet("{id}")]
public async Task<ActionResult<SuperHeroReadDTO>> Get(int id)
{
    var hero = await _repo.GetSuperHero(id);
    
    if (hero == null)
    {
        return NotFound("Hero Not Found");
    }
    else
    {
        // This is where it might be breaking
        return Ok(_mapper.Map<SuperHeroReadDTO>(hero));
    }
}

Unit Test

// Instantiated
readonly Mock<ISuperHeroRepo> repoMock = new();

readonly Mock<IMapper> mapperMock = new();

private readonly Random random = new();

........

[Fact]
public async Task Get_WithExistingHero_ReturnsExpectedSuperHero()
{
    // Arrange
    SuperHero expected = CreateRandomSuperHero();

    repoMock.Setup(repo => repo.GetSuperHero(It.IsAny<int>()))
        .ReturnsAsync(expected);

    var controller = new SuperHeroController(repoMock.Object, mapperMock.Object);

    // Act - This returns null. Debugging in the controller, I get the object back from repo
    var result = await controller.Get(random.Next());

    // Assert
    Assert.IsType<SuperHeroReadDTO>(result.Value);
    var dto = result.Value;
    Assert.Equal(expected.SuperHeroName, dto.SuperHeroName);
    Assert.Equal($"{expected.LegalFirstName} {expected.LegalLastName}", dto.LegalName);
    Assert.Equal($"{expected.JurisdictionCity}, {expected.JurisdictionState}", dto.Jurisdiction);
}

Edited: Added the controller instantiation, and the Mapper Profile. Not sure if it'll add more info.

Upvotes: 2

Views: 767

Answers (1)

qtran-dev
qtran-dev

Reputation: 31

@shree.pat18 was indeed correct. I did end up needing to set up the Mapper object in the Unit Test and passing it back into the controller. I'll need to refactor, but here below is what resolved it:

[Fact]
public async Task Get_WithExistingHero_ReturnsExpectedSuperHero()
{
    // Arrange
    SuperHero expected = CreateRandomSuperHero();
    
    repoMock.Setup(repo => repo.GetSuperHero(It.IsAny<int>()))
        .ReturnsAsync(expected);

    // Setup of the mock Mapper
    var mockMapper = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile(new SuperHeroProfiles());
    });

    var mapper = mockMapper.CreateMapper();

    var controller = new SuperHeroController(repoMock.Object, mapper);

    // Act
    var result = await controller.Get(random.Next());

    // Assert
    Assert.IsType<SuperHeroReadDTO>(result.Value);
    var dto = result.Value;
    Assert.Equal(expected.SuperHeroName, dto.SuperHeroName);
    Assert.Equal($"{expected.LegalFirstName} {expected.LegalLastName}", dto.LegalName);
    Assert.Equal($"{expected.JurisdictionCity}, {expected.JurisdictionState}", dto.Jurisdiction);
}

Upvotes: 1

Related Questions