Search code examples
.net-coremoqxunit

Mock repository method failed verify test


I'm using Moq framework and xUnit in .NET Core application. I have a mock of the repository

public interface IItemRepository<T>
{
    string Add(T entity);
}

I want to test the method of the class, which is using this repository

public DefaultCartService(IItemRepository<CartItem> cartRepository)
{
    _cartRepository = cartRepository;
}

public Task<GenericResponse<string>> AddItem(CartItem model)
{
   var result = new GenericResponse<string>();

    try
    {
        result.Response = _cartRepository.Add(cartItem);
    }
    catch(Exception ex)
    {
        result.Status = false;
        result.Error = ex.Message;
    }

    return Task.FromResult(result);
}

This is a Generic Response Class to wrap repository result

public class GenericResponse<T>
{
    public bool Status { get; set; } = true;
    public T Response { get; set; }
    public string Error { get; set;
    }
}

This is how I preset Mock for the repository and calling the method

[Fact]
public  void AddItem_WorkingTest()
{
    //creating mock
    var mock = new Mock<IItemRepository<CartItem>>();

    //setting a method
    mock.Setup(x => x.Add(new CartItem())).Returns("SomeGuidValue");

    //creating input param
    CartItemInputModelDTO inputModel = new CartItem() {BuyerId = "232", ProductId = "35435" };

    //creating service class instance and passing repository mock
    var _service = new DefaultCartService(_cartRepository.Object);

    //calling method
    var result =  _service.AddItem(inputModel).Result.Response;

    //verifying that mocks method been called
    mock.Verify(x => x.Add(new CartItem()), Times.AtLeastOnce);
}

As a result, I get a failed test as repository never been called. And I don't understand why, since it I set there is nothing the stops method to be called Test result

Moq.MockException : Expected invocation on the mock at least once, but was never performed: x => x.Add(CartItem)
Performed invocations: Mock<IItemRepository<CartItem>:2 (x): No invocations performed.


Solution

  • use It.IsAny<CartItem>() in the setup. The current setup wont work because the instance passed in the test is not the instance used in the setup.

    Also if testing a member that returns Task derived result then make test async

    [Fact]
    public  async Task AddItem_WorkingTest() {
        //creating mock
        var mock = new Mock<IItemRepository<CartItem>>();
    
        //setting a method
        mock.Setup(x => x.Add(It.IsAny<CartItem>())).Returns("SomeGuidValue");
    
        //creating input param
        var inputModel = new CartItem() {BuyerId = "232", ProductId = "35435" };
    
        //creating service class instance and passing repository mock
        var _service = new DefaultCartService(mock.Object);
    
        //calling method
        var result =  await _service.AddItem(inputModel);;
    
        //verifying that mocks method been called
        mock.Verify(x => x.Add(It.IsAny<CartItem>()), Times.AtLeastOnce);
    }
    

    If you want to verify the members of the instance passed into the mock then use It.Is<CartItem>() with a predicate

    For example

    //... omitted for brevity
    
    mock.Verify(x => x.Add(It.Is<CartItem>(_ => 
        _.BuyerId == inputModel.BuyerId 
        && _.ProductId == inputModel.ProductId)), Times.AtLeastOnce);
    

    Reference Moq Quickstart