Mohsen Esmailpour
Mohsen Esmailpour

Reputation: 11564

Mock Linq `Any` predicate with Moq

I'm trying to mock AnyAsync method in my repository with below codes but repository always returns false.

The signature of AnyAsync is:

Task<bool> AnyAsync<TEntity>(Expression<Func<TEntiry, bool>> predicate)

I tried the following setups:

1:

mockCustomerRepository.Setup(r => r.AnyAsync(c => c.Email == "[email protected]"))
   .ReturnsAsync(true);

2:

Expression<Func<CustomerEntity, bool>> predicate = expr => 
    expr.CustomerPerson.Email == "[email protected]";

mockCustomerRepository.Setup(r => r.AnyAsync(It.Is<Expression<Func<CustomerEntity, bool>>>
   (criteria => criteria == predicate))).ReturnsAsync(true);

3:

mockCustomerRepository.Setup(r => r.AnyAsync(It.IsAny<Expression<Func<CustomerEntity, bool>>>()))
    .ReturnsAsync(true);

My test:

public class Test 
{
    Mock<ICustomerRepository> mockCustomerRepository;

    public Test()
    {
        mockCustomerRepository = new Mock<ICustomerRepository>();
    }

    [Fact]
    public async Task CustomerTest()
    {   
        var customer = ObjectFactory.CreateCustomer(email: "[email protected]");
        var sut = new CustomerService(mockCustomerRepository.Object);

        var result = await sut.ValidateCustomerAsync(customer);
        .
        .
        .
    }
}

My CustomerService.ValidateCustomerAsync method:

public async Task<OperationResult> ValidateCustomerAsync(CustomerEntity customer)
{
    var errors = new List<ValidationResult>();

    if (await _repository.AnyAsync(c => c.Email == customer.Email))
        errors.Add(new ValidationResult("blah blah")));

I've also read this but it doesn't work too.

Upvotes: 3

Views: 3696

Answers (4)

Old Fox
Old Fox

Reputation: 8725

The following snippet shows the correct way to mock your AnyAsync method:

[TestMethod]
public async Task TestMethod1()
{
    var fakeCustomerRepo = new Mock<ICustomerRepository>();
    var foo = false;
    fakeCustomerRepo.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<CustomerEntity, bool>>>()))
        .Callback<Expression<Func<CustomerEntity, bool>>>(
            expression =>
            {
                var func = expression.Compile();
                foo = func(new CustomerEntity() {Email = "[email protected]"});
            })
        .Returns(() => Task.FromResult(foo));

    var customer = new CustomerEntity() {Email = "[email protected]"};
    var result = await fakeCustomerRepo.Object.AnyAsync<CustomerEntity>(c => c.Email == customer.Email);
    Assert.IsTrue(result);

    customer = new CustomerEntity() { Email = "[email protected]" };
    result = await fakeCustomerRepo.Object.AnyAsync<CustomerEntity>(c => c.Email == customer.Email);
    Assert.IsFalse(result);
}

using the above setup you can verify your predicate which is part of your unit behavior.

Upvotes: 5

Ioana Stoian
Ioana Stoian

Reputation: 156

Maybe I am wrong but, but as far as I have read for methods that return only a Task, .Returns(Task.FromResult(default(object))) can and should be used.

So in your case it would be mocksCustomerRepository.Setup(r => r.AnyAsync(c => c.Email == "[email protected]")).Returns(Task.FromResult(true));

Upvotes: 0

meJustAndrew
meJustAndrew

Reputation: 6623

All I can see that should cause this problem are two options:

1. The repository method does not come from an interface that you should mock to return the desired value, as it is not marked with virtual.

2. The types (TEntity) you use in method do not match when you are using the moq.

AnyAsync<TEntity>(Expression<Func<TEntity, bool>> 

as you can setup for let's say MemberEntity and call the ValidateCustomerAsync with a CustomerEntity.

Upvotes: 0

Blair Conrad
Blair Conrad

Reputation: 242100

I think you're running into the difficulties of matching predicates. Funcs or expressions of Funcs use reference-equality, so just using == to compare the two instances isn't going to work. (As a general rule, if you can't get predicate1.Equals(predicate2) to return true, Moq's argument matchers aren't going to match.)

This is a little unorthodox, but I refer you to my answer to a similar question for FakeItEasy matchers, which in turn points you at a number of techniques for validating predicates.

Upvotes: 2

Related Questions