使用Lambda表达式的Moq?

3

我正在尝试使用NuGet上的Moq 4.0.10827来测试一个应用程序服务,并需要查询一个仓库:

public class MyService
{
    Repository<MyObject> _Repo;

    public MyObject Get (string SomeConstraint)
    {
        return _Repo
            .GetTheFirstOneOrReturnNull (M => M.Constraint.Equals (
                SomeContraint, StringComparison.InvariantCultureIgnoreCase
            ));  // GetTheFirstOneOrReturnNull takes a Func<MyObject, bool>
    }
}

如何使用Moq复制lambda表达式?我一直收到“不支持的表达式”异常。

这是我已经做的事情的想法:

[TestMethod]
public void GetByMyConstraintShouldReturnWithMyObject ()
{
    // Arrange
    const string MyConstraint = "Constraint";
    MyObject Expected = new MyObject { Constraint = MyConstraint };
    Mock<Repository<MyObject>> MockRepo = new Mock<Repository<MyObject>> ();
    MockRepo.Setup (x => x.GetTheFirstOneOrReturnNull (M => M.Constraint.Equals (MyConstraint, StringComparison.InvariantCultureIgnoreCase)))
            .Returns (Expected).Verifable ();
    MyService Service = new MyService (MockRepo.Object);

    // Act
    MyObject Result = Service.Get (MyConstraint);


    // Assert
    Assert.AreSame (Expected, Result);
    MockRepo.Verify ();

}

我看了一些其他答案,但我仍然无法确定自己做错了什么(承认我是Moq的“新手”)。我得出结论,这将是痛苦的,但我有很多像这样的测试要做,现在想要稳定下来,而不是以后陷入困境。
唯一的选择是将lambda表达式封装在对象中,并传递存储库让它执行查询吗?我不想为我的测试环境改变自己的代码,但我也不想浪费时间试图强制其按照我的意愿工作。
3个回答

1

看起来你正在尝试模拟扩展方法。你不能以这种方式模拟扩展方法,因为它们实际上并没有在你要模拟的类型上声明。

在这种情况下,由于你的存储库似乎代表了一组事物,所以你应该替换为一个简单的硬编码集合。由于你没有提供 IRepository 的源代码,所以不清楚你可能会如何处理。


我不确定添加存储库如何在这里有所帮助。它只是从其后备存储(内存或数据库,无论哪种)中返回第一个或默认值。FirstOrDefault只是我在示例中给它的方法名称-实际上是由存储库类定义并采用Func<T, bool>。 - Jim D'Angelo

1

我建议你更新你的Repository,添加一个类似FindByConstraint方法的东西,你只需传入Constraint和Repo本身就可以使用FirstOrDefault()进行查询。我认为这样做有合理的设计原因,不仅可以使测试更容易,而且搜索是不区分大小写的,并且会愉快地返回null,你可以看作是Repo应该做出的决策而不是它的客户端。

我个人认为测试对象的困难意味着我的设计有缺陷,我认为可轻松测试的对象值得追求。


我完全赞同你最后的陈述。我计划重构并使存储库更加友好,这只是我在测试需要使用 Func<T, bool> 的情况下遇到的问题,我想知道 Moq 是否能够很好地与之配合。 - Jim D'Angelo

1

这个应该不需要太多测试。一个正确的测试将展示代码委托给 FirstOrDefault 是有效的,然后所有后续的测试都是在测试 Func 约束中的逻辑是否正确,而这可以在不将其传递到 Repository 中完成。


嗯...也许我的问题没有表达我真正想测试的内容。我不在乎repo,这就是为什么我要模拟它。我只想测试服务。这次我选择了自定义存根而不是Moq。它似乎不能很好地处理Func<T, TResult>。 - Jim D'Angelo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接