3

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 == "some@one.com"))
   .ReturnsAsync(true);

2:

Expression<Func<CustomerEntity, bool>> predicate = expr => 
    expr.CustomerPerson.Email == "some@one.com";

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: "some@one.com");
        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.

Mohsen Esmailpour
  • 11,224
  • 3
  • 45
  • 66

4 Answers4

5

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 = "foo@gmail.com"});
            })
        .Returns(() => Task.FromResult(foo));

    var customer = new CustomerEntity() {Email = "foo@gmail.com"};
    var result = await fakeCustomerRepo.Object.AnyAsync<CustomerEntity>(c => c.Email == customer.Email);
    Assert.IsTrue(result);

    customer = new CustomerEntity() { Email = "boo@gmail.com" };
    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.

Old Fox
  • 8,629
  • 4
  • 34
  • 52
2

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.

Community
  • 1
  • 1
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • thank you, I got whats the problem, but I'm not going for implementing another logic expression equality checking. I want know how to solve this with moq. – Mohsen Esmailpour Aug 09 '16 at 16:42
0

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.

meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
0

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 == "some@one.com")).Returns(Task.FromResult(true));

Ioana Stoian
  • 146
  • 1
  • 10
  • 1
    `ReturnAsync(expectedResult)` method is a wrapper for `.Returns(Task.FromResult(expectedResult))` therefore your answer won't work.... – Old Fox Aug 10 '16 at 13:08