user1574598
user1574598

Reputation: 3879

Moq Exception: Expected invocation on the mock once, but was 0 times... - .Net Core 3.1 with xUnit

I am new to Moq and I am trying to implement 2 tests that check for a HttpStatusCode.NotModified and HttpStatusCode.Ok. However, the latter passes its test, but the former doesn't and returns the exception:

Moq.MockException : Expected invocation on the mock once, but was 0 times: x => x.UpdateStateAsync(It.Is(y => y == RedisServiceModel))

Here is the HttpStatusCode.Ok which passes:

[Fact]
public void UpdateState_StateHasBeenUpdated_HttpOk()
{
    //arrange
    var state = new RedisServiceModel();
    var redisServiceMock = new Mock<IRedisService>();
    redisServiceMock.Setup(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == state))).ReturnsAsync(state);
    var testController = new TestController(redisServiceMock.Object);

    // act
    var statusResult = testController.UpdateStates(state);

    // assert
    redisServiceMock.Verify(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == state)));
    Assert.True(statusResult.Result is HttpStatusCode.OK);
}

Here is the HttpStatusCode.NotModified which throws the exception:

[Fact]
public void UpdateState_StateHasNotBeenModified_HttpNotModified()
{
    //arrange
    var state = new RedisServiceModel();
    var redisServiceMock = new Mock<IRedisService>();
    redisServiceMock.Setup(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == state))).ReturnsAsync(state);
    var testController = new TestController(redisServiceMock.Object);

    // act
    var statusResult = testController.UpdateStates(null);

    // assert
    redisServiceMock.Verify(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == state)), Times.Once);
    Assert.True(statusResult.Result is HttpStatusCode.NotModified);
}

Here is the PUT api call:

[HttpPut]
[Route("updatestates")]
public async Task<HttpStatusCode> UpdateStates(RedisServiceModel update)
{
    RedisServiceModel updateState = await _redisService.UpdateStateAsync(update);

    if (updateState != null)
    {
        return HttpStatusCode.OK;
    }

    return HttpStatusCode.NotModified;
}

I'm guessing it's because I'm passing in null here testController.UpdateStates(null). I did try wrapping everything in the UpdateStates API method to null check the update argument, but this still yielded the same exception. If anymore code is needed let me know and I'll edit this post.

Upvotes: 4

Views: 7169

Answers (2)

Nkosi
Nkosi

Reputation: 247631

The subject should be awaited, otherwise the assertion could be made before the subject has completed the expected behavior

The mock should also be configured to return the passed argument to mimic the expected behavior of the actual implementation

[Fact]
public async Task UpdateState_StateHasNotBeenModified_HttpNotModified() {
    //arrange
    var state = new RedisServiceModel();
    var redisServiceMock = new Mock<IRedisService>();
    redisServiceMock
        .Setup(x => x.UpdateStateAsync(It.IsAny<RedisServiceModel>()))
        .ReturnsAsync(x => x);
    var testController = new TestController(redisServiceMock.Object);

    // act
    var statusResult = await testController.UpdateStates(null);

    // assert
    redisServiceMock.Verify(x => x.UpdateStateAsync(
        It.Is<RedisServiceModel>(y => y == null)), 
        Times.Once);
    Assert.True(statusResult is HttpStatusCode.NotModified);
}

The same should also have been done for the other test

[Fact]
public async Task UpdateState_StateHasBeenUpdated_HttpOk()
{
    //arrange
    var state = new RedisServiceModel();
    var redisServiceMock = new Mock<IRedisService>();
    redisServiceMock
        .Setup(x => x.UpdateStateAsync(It.Is<RedisServiceModel>()))
        .ReturnsAsync(x => x);
    var testController = new TestController(redisServiceMock.Object);

    // act
    var statusResult = await testController.UpdateStates(state);

    // assert
    redisServiceMock.Verify(x => x.UpdateStateAsync(
        It.Is<RedisServiceModel>(y => y == state)));
    Assert.True(statusResult is HttpStatusCode.OK);
}

That said, the subject action should be refactored to follow proper syntax for controllers.

Assuming the controller is derived from ControllerBase it would have access to helper methods for action results.

[HttpPut]
[Route("updatestates")]
public async Task<IActionResult> UpdateStates(RedisServiceModel update) {
    RedisServiceModel updateState = await _redisService.UpdateStateAsync(update);

    if (updateState != null) {
        return Ok();
    }

    return StausCode((int)HttpStatusCode.NotModified);
}

Upvotes: 3

Athanasios Kataras
Athanasios Kataras

Reputation: 26460

The problem is that you are checking between null and state which are not equal. The function has been called but with null, instead of state (the equality is on the reference).

Try with this:

        [Fact]
        public void UpdateState_StateHasNotBeenModified_HttpNotModified()
        {
            //arrange
            var state = new RedisServiceModel();
            var redisServiceMock = new Mock<IRedisService>();
            redisServiceMock.Setup(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == state))).ReturnsAsync(state);
            var testController = new TestController(redisServiceMock.Object);

            // act
            var statusResult = testController.UpdateStates(null);

            // assert
            redisServiceMock.Verify(x => x.UpdateStateAsync(It.Is<RedisServiceModel>(y => y == null)), Times.Once);
            Assert.True(statusResult.Result is HttpStatusCode.NotModified);
        }

Upvotes: 1

Related Questions