Moq.Mock <expression <func <t,bool>&gt;&gt;() - 如何使用Moq将表达式设置为模拟</expression <func <t,bool>

时间:2011-11-02 14:59:50

标签: .net unit-testing tdd mocking moq

我已经阅读了很多关于这个主题的QA,我仍然无法找到解决问题的方法,所以我决定公开我的案例。

我有这个界面

public interface IRepository<T> where T : class, IEntity 
{
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);
    T FindIncluding(int id, params Expression<Func<T, object>>[] includeProperties);
}

这是包含我想要设置的模拟的方法的基本结构

public PeopleController CreatePeopleController()
{
    var mockUnitofWork = new Mock<IUnitOfWork>();
    var mockPeopleRepository = new Mock<IRepository<Person>>();

    mockPeopleRepository.Setup(r=>r.Find().Returns(new Person(){});
    mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
    return new PeopleController(mockUnitofWork.Object);
}

我一直试图用这种方式设置模拟:

public PeopleController CreatePeopleController()
{
    var mockUnitofWork = new Mock<IUnitOfWork>();
    var mockPeopleRepository = new Mock<IRepository<Person>>();

    mockPeopleRepository.Setup(r=>r.Find(It.isAny<Expression<Func<Person,bool>>>()).Single()).Returns(new Person(){});
    mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
    return new PeopleController(mockUnitofWork.Object);
}

但系统总是抛出相同的异常“System.NotSupportedException:Expression引用一个不属于模拟对象的方法....”

另外我想补充说我正在使用MSTest和Moq

我知道使用Expression设置模拟并不容易,不推荐使用,但这对我来说非常重要,因为“查找”是我在我的应用程序中大量使用的方法

2 个答案:

答案 0 :(得分:19)

问题在于您尝试将Single()扩展方法设置为模拟的一部分。设置调用需要具有您的方法的结果 - 不是您的方法的结果,随后应用了一些扩展方法。我试试这个:

    [TestMethod]
    public void MyTestMethod()
    {
        var myMock = new Mock<IRepository<Person>>();
        myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());

        Assert.IsTrue(true);
    }

在这里,您只需使用setup对Find()方法进行存根,并在Returns()子句中执行所有其他操作。我总体上建议采用这种方法。安装程序应该完全镜像你的模拟项目,你可以为返回()(或抛出()或其他任何)调用做一堆黑魔法,让它做你想要的。

(当我在VS中运行该代码时,它已通过,因此它没有抛出异常)

答案 1 :(得分:4)

在没有It.IsAny<>的情况下使用Moq&#39; .CallBack会强制您编写测试未涵盖的代码。相反,它允许任何查询/表达式通过,从单元测试的角度来看,使得模拟基本没用。

解决方案:你需要使用Callback来测试表达式,或者你需要更好地约束你的模拟。无论哪种方式都是混乱和困难的。只要我一直在练习TDD,我就已经处理过这个问题了。我终于把一个帮助类放在一起,使它更具表现力和更少杂乱。这是最终结果(适合您的示例):

mockPeopleRepository
  .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
    .ThatMatches(correctPerson)
    .And().ThatDoesNotMatch(deletedPerson)
    .Build()))
  .Returns(_expectedListOfPeople); 

以下是关于它的博客文章,并提供源代码:http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

相关问题