0

I have an API Controller which publishes a command using NServiceBus. I am using NUnit and NSubstitute for testing. I want to test that certain properties from the model are populated on the command

Here is my controller with a route.

[RoutePrefix("api/fileService")]
public class FileServiceController : ApiController
{
    [HttpPost]
    [Route("releasefile")]
    public async Task<IHttpActionResult> ReleaseFile(FileReleaseAPIModels.ReleaseFileModel model)
    {
        var currentUser = RequestContext.Principal?.Identity as ClaimsIdentity;


        if (model.FileType.Equals("ProductFile"))
        {
            _logger.Info($"Releasing Product files for date: {model.FileDate.ToShortDateString()} ");
            _bus.Send<IReleaseProductFiles>("FileManager.Service", t =>
            {
                t.FileId = Guid.NewGuid();
                t.RequestedDataDate = model.FileDate;
                t.RequestingUser = currentUser?.Name;
                t.RequestDateTime = DateTime.Now;
            });
        }
        return Ok();
    }

}

In my test, I substitute(mock) Ibus and try to validate the call received. Here is the test method:

    [Test]
    public async Task TestReleaseProductsFile()
    {
        var bus = Substitute.For<IBus>();
        var dbContent = _container.Resolve<IFileManagerDbContext>();
        var apiContext = new FileServiceController(bus, dbContent);

        //Create a snapshot
        var releaseDate = DateTime.Now.Date;
        var result = await apiContext.ReleaseFile(new ReleaseFileModel
        {
            FileDate = releaseDate,
            FileType = "ProductFile"
        });

        Assert.That(result, Is.Not.Null, "Result is null");

        Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");

        bus.Received(1)
            .Send<IReleaseProductFiles>(Arg.Is<string>("FileManager.Service"), Arg.Is<Action<IReleaseProductFiles>>(
                action =>
                {
                    action.FileId = Guid.NewGuid();
                    action.RequestedDataDate = releaseDate;
                    action.RequestingUser = String.Empty;
                    action.RequestDateTime = DateTime.Now;
                }));
    }

This results in error - even though the message is actually sent. Here is the error message:

NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 1 call matching:
    Send<IReleaseProductFiles>("Capelogic.Service", Action<IReleaseProductFiles>)
Actually received no matching calls.
Received 1 non-matching call (non-matching arguments indicated with '*' characters):
    Send<IReleaseProductFiles>("Capelogic.Service", *Action<IReleaseProductFiles>*)

I am obviously missing something obvious here.

Bitmask
  • 918
  • 2
  • 11
  • 22

1 Answers1

2

The problem here is with the Action<IReleaseProductFiles> argument to Send -- we can't automatically tell if two different actions are the same. Instead, NSubstitute relies on the references being equivalent. Because both the test and the production code create their own Action instance, these will always be different and NSubstitute will say the calls don't match.

There are a few different options for testing this. These examples relate to Expression<Func<>>, but the same ideas apply to Action<>s.

In this case I'd be tempted to test this indirectly:

[Test]
public async Task TestReleaseProductsFile()
{
    var bus = Substitute.For<IBus>();
    var returnedProductFiles = Substitute.For<IReleaseProductFiles>();

    // Whenever bus.Send is called with "FileManager.Service" arg, invoke
    // the given callback with the `returnedProductFiles` object.
    // We can then make sure the action updates that object as expected.
    bus.Send<IReleaseProductFiles>(
            "FileManager.Service",
            Arg.Invoke<IReleaseProductFiles>(returnedProductFiles));

    // ... remainder of test ...

    Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");
    Assert.That(returnedProductFiles.FileId, Is.Not.EqualTo(Guid.Empty));
    Assert.That(returnedProductFiles.RequestedDataDate, Is.EqualTo(releaseDate));
    Assert.That(returnedProductFiles.RequestingUser, Is.EqualTo(String.Empty));
}

I'd recommend having a look through the previously mentioned answer though to see if there is a better fit for your situation.

Community
  • 1
  • 1
David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • Perfect. I just had to make one modification - Instead of: Arg.Invoke>(returnedProductFiles)); Changed TO: Arg.Invoke(returnedProductFiles)); – Bitmask Aug 18 '16 at 13:36