29

I am trying to build an xUnit Test project for an MVC Core 2.2 Application that is based on the CQRS/ES pattern. I utilize MediatR as part of my CQRS/ES pattern in the MVC application.

In one of my commands which I would like to test, I inject MediatR to publish an event once a customer record has been updated. Kind of like this:

public class UpdateCustomerCommandHandler : IRequestHandler<UpdateCustomerCommand>
{
    public IMediator Mediator { get; set;  }

    public UpdateCustomerCommandHandler(IMediator mediator)
    {
        Mediator = mediator;
    }

    public Task<Unit> Handle(UpdateCustomerCommand request, CancellationToken cancellationToken)
    {
        //do some stuff

        Mediator.Publish(new CustomersChanged());

        return Task.FromResult(new Unit());
    }
}

When writing a unit test for this command, I obviously also must create an instance of MediatR (or a mockup) which I then pass to to the command during test execution.

[Fact]
public async void UpdateCustomerCommand_CustomerDataUpdatedOnDatabase()
{
    //Arange

    var mediator = new Mediator(); // doesn't work that way..

    UpdateCustomerCommand command = new UpdateCustomerCommand();
    UpdateCustomerCommandHandler handler = new UpdateCustomerCommandHandler(mediator);

    //Act
    Unit x = await handler.Handle(command, new System.Threading.CancellationToken());

    //Asert
    //Do the assertion
}

However, instanciating MediatR (outside of the MVC application, where I can utilize the existing dependency injection implementation) seems to be not that simple and frankly speaking I actually do not understand how I can do in my test method.

I understand that I potentially could use a dependency injection framework for which MediatR already provides an implementation (Ninject, etc.), but I actually do not want to use any other third party libraries in my unit tests apart from MediatR, just for the sake of creating an instance.

Is there a simpler way to instantiate MediatR which I might have overseen?

VivekDev
  • 20,868
  • 27
  • 132
  • 202
Roland
  • 437
  • 1
  • 4
  • 7

2 Answers2

37

You're on the right lines with or a mockup - you need to mock the IMediator

There's a few mocking libraries out there:

  • Moq
  • FakeItEasy
  • NSubstitute

Moq is one of the most popular, so, using your test as an example:

[Fact]
public async void UpdateCustomerCommand_CustomerDataUpdatedOnDatabase()
{
    //Arrange
    var mediator = new  Mock<IMediator>();

    UpdateCustomerCommand command = new UpdateCustomerCommand();
    UpdateCustomerCommandHandler handler = new UpdateCustomerCommandHandler(mediator.Object);

    //Act
    Unit x = await handler.Handle(command, new System.Threading.CancellationToken());

    //Assert
    //Do the assertion

    //something like:
    mediator.Verify(x=>x.Publish(It.IsAny<CustomersChanged>()));
}
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Alex
  • 37,502
  • 51
  • 204
  • 332
  • 2
    Okay, I understand the concept (and am pretty amazed at the same time). But what if I actually want to check the state of the receiver of any of the notification (let's say, to check I want to check if some cache was deleted, based on the CustomersChanged Event)? This basically forces me to make a real instance of MediatR => Is there a simple way to do so? – Roland Mar 31 '19 at 17:59
  • 4
    @Roland then you're no longer `unit testing` you're integration testing. You'll need to create a 'real' Mediator, wire up all the dependencies etc... Avoid this. Test the parts in isolation. – Alex Mar 31 '19 at 18:05
  • 2
    You're right. I was going the wrong way with this! Thanks for your answer; helped me a lot! – Roland Apr 01 '19 at 07:54
  • 8
    @Alex tests in isolation as first step but at least some integration tests are good and very nice to have. We have faced this issue in one project where everything was nicely covered with unit tests but there was almost any integration tests which resulted in really big issues at some point. A lot of unit tests, but definitely some integration. – Marek Urbanowicz May 25 '19 at 12:48
  • 1
    If the purpose is to "test a command", doesn't this solution just test that a command is called. It tests that the command handler calls the Publish method. That's just boiler-plate code. The really important code that needs to be tested is the code inside a particular MediatR command or query. That's where the real work is done and where one command or query differs from another. – John Pankowicz Jul 14 '21 at 20:40
9

To expand on the accepted answer, if you absolutely need to test interaction between two services using MediatR, you can always moq each publish/handler pair using callbacks:

_mediator = new Mock<IMediator>();
_mediator.Setup(m => m.Publish(It.IsAny<YourNotification>(), It.IsAny<CancellationToken>()))
   .Callback<YourNotification, CancellationToken>((notification, cToken) => 
      _yourHandlerService.Handle(notification, cToken));

The above code, basically says if _mediator gets YourNotification via Publish method, then it will forward it to _yourHandlerService via Handle method. You can repeat that Setup for each type of mediator INotification you want to handle.

Eternal21
  • 4,190
  • 2
  • 48
  • 63