2

I have an .NET Core v2.1. API with CQRS. To simplify the controller, i'm using MediatR. Now i'm trying to write some unit tests for my handlers with xUnit, but it's throwing this exception:

StackTrace do Resultado: at Research.Application.EventoContext.Commands.ConfirmarPresencaCommandHandler.Handle(ConfirmarPresensaCommand request, CancellationToken cancellationToken) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Research.Application.EventoContext.Commands.ConfirmarPresencaCommandHandler.Handle(ConfirmarPresensaCommand request, CancellationToken cancellationToken) at Research.Application.EventoContext.Commands.ConfirmarPresencaCommandHandler.Handle() in C:\Users\public\Desktop\Research\ResearchAPI\app\src\Research.Teste\Commands\EventoCommands.cs:line 38 --- End of stack trace from previous location where exception was thrown --- Mensagem do Resultado: System.MissingMethodException : Method not found: 'System.Threading.Tasks.Task1<!!0> MediatR.IMediator.Send(MediatR.IRequest1<!!0>, System.Threading.CancellationToken)'.

This is my command and handler. Since I don't have repositories, I created a data context interface with DbSets and the SaveChangeAsync() method:

public class ConfirmarPresensaCommand : IRequest<PontuacaoVM>
{
    public Guid id_usuario { get; set; }
    public Guid id_evento { get; set; }
    public List<Guid> sub_eventos { get; set; }
}

public class ConfirmarPresensaCommandHandler : IRequestHandler<ConfirmarPresensaCommand, PontuacaoVM>
{
    private readonly IMySqlContext _context;
    private readonly IMediator _mediator;

    public ConfirmarPresensaCommandHandler(IMySqlContext context, IMediator mediator)
    {
        _context = context;
        _mediator = mediator;
    }

    public async Task<PontuacaoVM> Handle(ConfirmarPresensaCommand request, CancellationToken cancellationToken)
    {
        var _presenca = await _context.PresencasUsuarios
                                      .Include(p => p.SubEvento)
                                      .Where(p => p.CodigoUsuario == request.id_usuario &&
                                                  p.SubEvento.CodigoEvento == request.id_evento)
                                      .FirstOrDefaultAsync();

        if (_presenca == null)
        {
            var _subEvento = await _context.SubEventos
                                           .Where(s => s.CodigoEvento == request.id_evento &&
                                                       s.Principal)
                                           .FirstOrDefaultAsync();

            await _context.PresencasUsuarios.AddAsync(new PresencaUsuario
            {
                CodigoUsuario = request.id_usuario,
                CodigoSubEvento = _subEvento.Codigo,
            }, cancellationToken);

            await _context.SaveChangesAsync(cancellationToken);
        }

        return await _mediator.Send(new GerarPontuacaoCommand(request.id_usuario, "Evento", "Confirmar"));
    }
}

And this is my test. I'm using Moq.EntityFrameworkCore v2.0.1:

public class ConfirmarEventoTeste : TesteUtils
{
    public Mock<IMySqlContext> _moqContext;
    public Mock<IMediator> _moqMediator;
    
    public ConfirmarEventoTeste()
    {
        _moqContext = new Mock<IMySqlContext>();
        _moqMediator = new Mock<IMediator>();

        _moqMediator.Setup(m => m.Send(It.IsAny<ConfirmarPresencaCommand>(), It.IsAny<CancellationToken>()).ReturnsAsync(It.IsAny<PontuacaoVM>());
    }

    [Fact]
    public async Task ConfirmarPresenca_Sucesso()
    {
        var _command = new ConfirmarPresensaCommand();
        var _handler = new ConfirmarPresensaCommandHandler(_moqContext.Object, _moqMediator.Object);

        var _result = await _handler.Handle(_command, new CancellationToken());

        Assert.NotNull(_result);
    }
}

I know I need to write the setups for the unit test works, but for now my concern it's that the Handle() call on the test is not even hiting my Handler.

Can someone help me with that?

Nathy815
  • 21
  • 2

1 Answers1

1

To solve this issue, you need to mock Send mehod in MediatR

_mediatorMock = new Mock<IMediator>();
_mediatorMock.Setup(x => x.Send(It.IsAny<GerarPontuacaoCommand>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);

Also, this might be help you Mocking MediatR 3 with Moq

But I will be prefer another method of testing for MediatR. I will be prefer integration tests. Because with integration tests you can check correct behaviour of this package. Also, this approach use developers to tests behaviour in official examples in github.

They do the following steps (common algorithm):

  1. Create instance of Dependency injection container
  2. Register your handlers
  3. Get instance of IMediator
  4. Using Send method to send request
  5. Finally, check response Message

Dependency injection from Microsoft

If you use common DI from Microsoft.Extensions.DependencyInjection package, do the following steps:

  1. Create instance of DI container
var services = new ServiceCollection();
  1. Register handlers using package
services.AddMediatR(typeof(CustomerLoginRequest).Assembly);
  1. Build service provider in the last moment after all dependencies was added
var serviceProvider = services.BuildServiceProvider();
  1. Get meditor service from DI
var mediator = serviceProvider.GetRequiredService<IMediator>();
  1. Put your mediator to your handler class and execute it.
  2. Also, Assert the result.

Dependency injection from other package

If you use another package for DI - you can check example in github repository and create own test by example. They use StructureMap for DI.

DarkSideMoon
  • 835
  • 1
  • 11
  • 17
  • I've tried both approaches, still no luck. When I eliminate the internal mediator and call the handler directly it works just fine. But still not able to test controllers, for example, because of the MediatR. – Nathy815 Jan 14 '21 at 17:28
  • Did you create DI Container and inject `MediatR` in your `Controllers`? – DarkSideMoon Jan 14 '21 at 19:25