Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 232
user1574598 Avatar asked Nov 15 '25 15:11

user1574598


1 Answers

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);
}
like image 133
Nkosi Avatar answered Nov 18 '25 10:11

Nkosi