Home > Back-end >  How to filter by an expression when mocking a repository with Moq
How to filter by an expression when mocking a repository with Moq

Time:06-13

I have a method in a repository that receives an Expression as a parameter and the result of the method are supposed to filter by that expression. The thing is that when I try to test this method, it doesn't give me the result filtered:

Service method I'm testing

public async Task<Response<IEnumerable<AirlineDto>>> GetAllAsync()
{
    try
    {
        var airlines = await _airlineRepository.GetAsync(x => x.Status); // call to the real repo
        ... // some logic
    }
    ... // exception handling
}

Method Setup, MockAirlineRepository is a mock of AirlineRepository

public MockAirlineRepository MockGetAll()
{
    Setup(x => x.GetAsync(It.IsAny<Expression<Func<Airline, bool>>>()))
        .ReturnsAsync(GetTestAirlines);
    return this;
}

private static IEnumerable<Airline> GetTestAirlines()
{
    var airlines = new List<Airline>
    {
        new()
        {
            Id = Guid.NewGuid(),
            Name = "Test One",
            Status = true
        },
        new()
        {
            Id = Guid.NewGuid(),
            Name = "Test Two",
            Status = true
        },
        new()
        {
            Id = Guid.NewGuid(),
            Name = "Test Three",
            Status = false // notice that this Airline Status is false, so, 
                           // the count of the retrieved values should be 2
        }
    };
    return airlines;
}

Test

[Fact]
public void AirlineService_GetAll_ReturnsAirlines()
{
    //Arrange
    var mockAirlineRepo = new MockAirlineRepository().MockGetAll();
    var airlineService = new AirlineService(mockAirlineRepo.Object, _airlineMapper);

    //Act
    var result = airlineService.GetAllAsync().Result.Data;

    //Assert
    var airlineDtoList = result.ToList();
    Assert.NotEmpty(airlineDtoList);
    Assert.Equal(2, airlineDtoList.Count); // assert fails, because airlineDtoList.Count is 3
    mockAirlineRepo.VerifyGetAllAirlines(Times.Once());
}

CodePudding user response:

Let's start with the basics:

  • Dummy: simple code that returns bogus data
  • Fake: a working alternative which can take shortcuts
  • Stub: custom logic with predefined data
  • Mock: custom logic with expectations (interactive stub)
  • Shim: custom logic at run-time (replace static with a delegate)
  • Spy: interceptors to record calls

So, your mock is an interactive stub which means it can return different outputs based on the received input. In your Setup call you have made the following statement: whatever I receive as a filter expression I should always return with the entire list

If you want to apply the filter then change your setup

.Setup(x => x.GetAsync(It.IsAny<Expression<Func<Airline, bool>>>()))
.ReturnsAsync(filter => GetTestAirlines.Where(filter));

Now if you assert on airlineDtoList.Count then it will return 2. But in this case you are not testing the GetAllAsync function rather than your mock.

So, I would rather spend more time testing the // some logic


You can also take advantage of async-await in your test

[Fact]
public Task AirlineService_GetAll_ReturnsAirlines()
{
    //Arrange
    var mockAirlineRepo = new MockAirlineRepository().MockGetAll();
    var airlineService = new AirlineService(mockAirlineRepo.Object, _airlineMapper);

    //Act
    var response = await airlineService.GetAllAsync();
    var result = response.Data;

    //Assert
    ...
}
  • Related