Search code examples
c#unit-testingasp.net-web-apinunitmediatr

MediatR is returning null while unit testing controller


I am trying to unit test my controller but I am using MediatR. I have mocked it but somehow result is null. I tried to search other people questions but non of them helped.

My controller:

[HttpGet("{authorId}")]
    [ProducesResponseType(200, Type = typeof(Author))]
    [ProducesResponseType(400)]
    public async Task<IActionResult> GetAuthor(int authorId)
    {
        var query = new GetAuthorQuery(authorId);
        var result = await _mediator.Send(query); 
        return result != null ? (IActionResult)Ok(result) : NotFound();
    }

Unit Test constructor:

private Mock<IMediator> _mediator;
 public BookStoreAuthorControllerTests()
    {
        _mediator= new Mock<IMediator>();
        _authorRepository= new Mock<IAuthorRepository>();
        fixture= new Fixture();

        fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
        .ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior());

        var mapperConfig = new MapperConfiguration(c =>
        {
            c.AddProfile<Helpers.MappingProfiles>();
        });
        _mapper = mapperConfig.CreateMapper();

    }

GetAuthor Test :

 [Test]
    public async Task GetAuthor_Returns_Ok()
    {
        var author = fixture.Build<Author>().Create();
        var mappedAuthor= _mapper.Map<AuthorDto>(author);

        var controller = new AuthorController(_mediator.Object);
        var query = new GetAuthorQuery(author.Id);

        _authorRepository.Setup(r=>r.GetAuthor(author.Id)).ReturnsAsync(author);
        _mediator.Setup(x => x.Send(query, It.IsAny<CancellationToken>())).ReturnsAsync(mappedAuthor);

        var result = await controller.GetAuthor(author.Id);
        var obj = result as ObjectResult;

        Assert.AreEqual(200, obj.StatusCode);
    }

Solution

  • The issue is in the mocking of that Send method, shown below.

    _mediator.Setup(
        x => x.Send(query, It.IsAny<CancellationToken>())
        ).ReturnsAsync(mappedAuthor);
    

    Given that the query variable is a GetAuthorQuery class/reference type, makes that is not the exact same instance that gets passed at runtime. Because of that, the mock doesn't execute the ReturnsAsync method in order to return the given Author object.

    2 possible ways to solve this; do one of below.

    1. Set up the mock using It.IsAny<GetAuthorQuery>() instead of query.
    _mediator.Setup(
         x => x.Send(It.IsAny<GetAuthorQuery>(),  It.IsAny<CancellationToken>())
         ).ReturnsAsync(mappedAuthor);    
    
    1. Define that GetAuthorQuery as a record instead of a class; record types use value equality. Doing so you can keep using that query variable in your mock setup
    public record GetAuthorQuery : IRequest<Author>