Search code examples
c#unit-testingmoqxunit

XUnit + Moq + FluentAssertions, checking for null on Task instead of proper object


I am new to unit testing and moq.

Using Postman to test DeleteItemAsync(),

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> DeleteItemAsync(string id)
    {
        var item = _ItemRepo.GetItemByIdAsync(id);
        if (item == null)
            return NotFound();
        await _itemRepo.DeleteItemAsync(id);
        return NoContent();
    }

when the item isn't found, I get the proper result, NotFound.

When running my unit test it's failing because, in the controller, it is checking for null on the Task object returned by the moq _repoStub call to GetItemByIdAsync(id).

    [Fact]
    public async Task DeleteItemAsync_ItemDoesntExist_ReturnsNotFound()
    {
        // Arrange
        _repoStub
            .Setup(repo => repo.GetItemByIdAsync(It.IsAny<String>()))
            .ReturnsAsync((Item)null);
        _repoStub
            .SetupSequence(repo => repo.DeleteItemAsync(It.IsAny<String>()))
            .Returns(Task.FromResult<NotFoundResult>(null));

        var controller = new ItemController(_repoStub.Object, _mapperStub);

        // Act
        var actionResult = await controller.DeleteItemAsync(It.IsAny<String>());

        // Assert
        actionResult.Should().BeOfType<NotFoundResult>();
    }

Solution

  • GetItemByIdAsync should be awaited in the subject under test

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> DeleteItemAsync(string id)
    {
        var item =  await _ItemRepo.GetItemByIdAsync(id); //<--!!!
        if (item == null)
            return NotFound();
        await _itemRepo.DeleteItemAsync(id);
        return NoContent();
    }
    

    otherwise it will return a Task and thus not be null as shown by your error.

    Also, note that It.IsAny should only be used in expectation expressions and not as a variable

    [Fact]
    public async Task DeleteItemAsync_ItemDoesntExist_ReturnsNotFound()
    {
        // Arrange
        _repoStub
            .Setup(repo => repo.GetItemByIdAsync(It.IsAny<String>()))
            .ReturnsAsync((Item)null);
        _repoStub
            .SetupSequence(repo => repo.DeleteItemAsync(It.IsAny<String>()))
            .Returns(Task.FromResult<NotFoundResult>(null));
    
        var controller = new ItemController(_repoStub.Object, _mapperStub);
    
        // Act
        var actionResult = await controller.DeleteItemAsync(""); //<--It.IsAny<String>() removed
    
        // Assert
        actionResult.Should().BeOfType<NotFoundResult>();
    }