Search code examples
unit-testingcontrollerxunitmediatrfakeiteasy

Call to FakeItEasy mocked mediatr.send method fails assertion


I am new to FakeItEasy and I am having a problem asserting if an async method has been called. It fails assertion because it hasn't been called. I have done my best to ensure the assertion matches the configured call but still no dice.

        [HttpPost]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status201Created)]
        public async Task<IActionResult> Post(CreateOwnerRequest requestModel)
        {
            var command = new CreateOwnerCommand { RequestModel = requestModel };
            var ownerResponse = await _mediator.Send(command, default);
                       
            //TODO: return URI for the new resources
            return Created("", ownerResponse);
        }
        [Theory]
        [ClassData(typeof(ValidCreateOwnerTestData))]
        public async void ShouldCallCreateOwnerHandler(CreateOwnerRequest validCreateOwnerModel)
        {
            // Arrange
            var fakeMediator = A.Fake<IMediator>();
            A.CallTo(() => fakeMediator.Send(new CreateOwnerCommand { RequestModel = validCreateOwnerModel },
                default)).Returns(A.Dummy<Task<OwnerResponse>>());
            var ownerController = new OwnerController(fakeMediator);

            // Act
            _ = await ownerController.Post(validCreateOwnerModel);

            // Assert
            A.CallTo(() => fakeMediator.Send(new CreateOwnerCommand { RequestModel = validCreateOwnerModel },
                default)).MustHaveHappened();
        }
    public class ValidCreateOwnerTestData : IEnumerable<object[]>
    {
        public IEnumerator<object[]> GetEnumerator()
        {
            yield return new object[] { new CreateOwnerRequest { FirstName = "Foo", LastName = "Bar" } };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

Solution

  • In your assertion, you create a new instance of CreateOwnerRequest. Obviously, this isn't the same instance that is actually used in the controller action, so it isn't considered equal, and the assertion fail.

    You could override Equals on CreateOwnerRequest so that they are considered equal, but it's probably not a good idea, since you would do it only in order to satisfy the assertion.

    A better approach is to use argument constraints to specify a condition that the argument must match:

    // Assert
    A.CallTo(() => fakeMediator.Send(
        A<CreateOwnerCommand>.That.Matches(command => /* some condition here */))
        A<CancellationToken>.Ignored))
        .MustHaveHappened();
    

    (note that I also changed default to A<CancellationToken>.Ignored, because you probably don't want the assertion to fail if the controller action starts using a real cancellation token...)