gus*_*nis 15 .net tdd unit-testing moq mocking
我已经阅读了很多关于这个主题的其他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);
}
Run Code Online (Sandbox Code Playgroud)
这是包含我想要设置的Mock的方法的基本结构
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);
}
Run Code Online (Sandbox Code Playgroud)
我一直试图用这种方式设置模拟:
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);
}
Run Code Online (Sandbox Code Playgroud)
但系统总是抛出相同的异常"System.NotSupportedException:Expression引用一个不属于模拟对象的方法......"
另外我想补充说我正在使用MSTest和Moq
我知道使用Expression设置模拟并不容易,不推荐,但这对我来说非常重要因为"查找"是我在我的应用程序中大量使用的方法
Eri*_*ich 20
问题是您正在尝试将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);
}
Run Code Online (Sandbox Code Playgroud)
在这里,您只需使用setup对Find()方法进行存根,并在Returns()子句中执行所有其他操作.我总体上建议采用这种方法.安装程序应该完全镜像你的模拟项目,你可以为返回()(或抛出()或其他)调用做一堆黑魔法,让它做你想要的.
(当我在VS中运行该代码时,它已通过,因此它没有抛出异常)
使用It.IsAny<>没有Moq 的代码.CallBack会迫使您编写测试未涵盖的代码。相反,它允许任何查询/表达式通过,从单元测试的角度来看,你的模拟基本上毫无用处。
解决方案:您要么需要使用回调来测试表达式,要么需要更好地约束您的模拟。无论哪种方式都是混乱且困难的。自从我实践 TDD 以来,我就一直在处理这个问题。我最终组合了一个辅助类,使其更具表现力并且不那么混乱。这是最终结果(适合您的示例):
mockPeopleRepository
.Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
.ThatMatches(correctPerson)
.And().ThatDoesNotMatch(deletedPerson)
.Build()))
.Returns(_expectedListOfPeople);
Run Code Online (Sandbox Code Playgroud)
这是讨论它并提供源代码的博客文章:http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/