Search code examples
c#unit-testingmoqxunitfluent-assertions

How to Unit Test Delete operation using moq and xUnit?


I'm trying to Unit Test CRUD operations for an API in my WEB API project. I have a hard time figuring out how to do that for DELETE. Below is my service;

public async Task DeleteCompany(int id)
{
    var query = "DELETE FROM Companies WHERE Id = @Id";
    using(var connection = _context.CreateConnection())
    {
        await connection.ExecuteAsync(query, new {id});
    }
}

Here's the controller

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteCompany(int id)
{
    var company = await _companyRepository.GetCompanyById(id);
    if (company == null)
    {
        return NotFound();
    }
    await _companyRepository.DeleteCompany(id);
    return NoContent();
}

And below is the code for Unit Testing

[Theory]
[InlineData(1)]
public async Task CompanyController_DeleteCompany_ShouldReturnStatusCode204(int id)
{
    var mockCompany = new Mock<ICompanyRepository>();
    mockCompany.Setup(service => service.DeleteCompany(id))
           .Returns(Task.CompletedTask)
           .Verifiable();

    var sut = new CompanyController(mockCompany.Object);

    var result = (OkObjectResult) await sut.DeleteCompany(id);

    result.StatusCode.Should().Be(204);
}

It turned out that it returns a null value of my model (Company in this example) after debugging. That's why the test doesn't pass and it gets to NotFound error in the controller.


Solution

  • You need to set up the Test Double's GetCompanyById method so that it doesn't return null:

    [Theory]
    [InlineData(1)]
    public async Task CompanyController_DeleteCompany_ShouldReturnStatusCode204(int id)
    {
        var mockCompany = new Mock<ICompanyRepository>();
        mockCompany.Setup(service => service.GetCompanyById(id))
            .ReturnsAsync(/*pass non-null value here*/);
        var sut = new CompanyController(mockCompany.Object);
    
        var result = (OkObjectResult) await sut.DeleteCompany(id);
    
        result.StatusCode.Should().Be(204);
        mockCompany.Verify(service => service.DeleteCompany(id);
    }
    

    On the other hand, you don't need to set up DeleteCompany as a verifiable action since it doesn't return anything anyway. Mocks for Commands, Stubs for Queries. Use mockCompany.Verify instead, as shown above.