我已经阅读了很多关于这个主题的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设置模拟并不容易,不推荐使用,但这对我来说非常重要,因为“查找”是我在我的应用程序中大量使用的方法
答案 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/