【问题标题】:Mock Linq `Any` predicate with Moq使用 Moq 模拟 Linq `Any` 谓词
【发布时间】:2016-12-15 15:12:34
【问题描述】:

我正在尝试使用以下代码在我的存储库中模拟 AnyAsync 方法,但存储库始终返回 false

AnyAsync的签名是:

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

我尝试了以下设置:

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);

我的测试:

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);
        .
        .
        .
    }
}

我的CustomerService.ValidateCustomerAsync 方法:

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")));

我也读过this,但也没有用。

【问题讨论】:

  • 你也可以显示repository.AnyAsync方法签名吗?
  • 'publis async Task AnyAsync(Expression> predicate) { return await _context.AnyAsync(predicate); }
  • 这个方法是否来自它实现的接口?很抱歉怀疑这一点,但它应该可以正常工作,所以我只能询问该方法是否来自您模拟的接口?
  • 是的,它来自接口。
  • 能否包含实例化模拟的代码?

标签: c# linq unit-testing mocking moq


【解决方案1】:

以下 sn-p 显示了模拟 AnyAsync 方法的正确方法:

[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);
}

使用上述设置,您可以验证作为单元行为一部分的谓词。

【讨论】:

  • 谢谢@Old Fox,你拯救了我的一天。
【解决方案2】:

我认为您遇到了匹配谓词的困难。 Funcs 或 Funcs 的表达式使用引用相等,因此仅使用 == 比较两个实例是行不通的。 (作为一般规则,如果您无法让 predicate1.Equals(predicate2) 返回 true,则 Moq 的参数匹配器不会匹配。)

这有点不正统,但我建议您参考my answer to a similar question for FakeItEasy matchers,它反过来为您指出了一些验证谓词的技术。

【讨论】:

  • 谢谢,我知道了问题所在,但我不打算实施另一个逻辑表达式相等检查。我想知道如何用最小起订量解决这个问题。
【解决方案3】:

我所看到的应该导致这个问题的只有两个选项:

1. 存储库方法不是来自您应该模拟以返回所需值的接口,因为它没有用virtual 标记。

2.您在方法中使用的类型(TEntity)在您使用起订量时不匹配。

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

您可以设置为MemberEntity 并使用CustomerEntity 调用ValidateCustomerAsync

【讨论】:

  • MemberEntity 是复制粘贴错误。我确定了答案。
【解决方案4】:

也许我错了,但是就我所阅读的仅返回任务的方法而言,.Returns(Task.FromResult(default(object))) 可以而且应该使用。

所以在你的情况下是mocksCustomerRepository.Setup(r =&gt; r.AnyAsync(c =&gt; c.Email == "some@one.com")).Returns(Task.FromResult(true));

【讨论】:

  • ReturnAsync(expectedResult) 方法是 .Returns(Task.FromResult(expectedResult)) 的包装,因此您的答案将不起作用....
猜你喜欢
  • 2012-02-18
  • 2011-10-22
  • 2011-10-13
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 2019-07-23
相关资源
最近更新 更多