Moq.Mock – 如何设置一个表达式的方法
我正在模拟我的存储库接口,我不知道如何设置一个接受表达式并返回一个对象的方法? 我正在使用Moq和NUnit
接口:
public interface IReadOnlyRepository : IDisposable { IQueryable All() where T : class; T Single(Expression<Func> expression) where T : class; }
使用IQueryable进行测试已经设置,但不知道如何设置T Single:
private Moq.Mock _mockRepos; private AdminController _controller; [SetUp] public void SetUp() { var allPages = new List(); for (var i = 0; i < 10; i++) { allPages.Add(new Page { Id = i, Title = "Page Title " + i, Slug = "Page-Title-" + i, Content = "Page " + i + " on page content." }); } _mockRepos = new Moq.Mock(); _mockRepos.Setup(x => x.All()).Returns(allPages.AsQueryable()); //Not sure what to do here??? _mockRepos.Setup(x => x.Single() //---- _controller = new AdminController(_mockRepos.Object); }
您可以这样设置:
_mockRepos.Setup(x => x.Single(It.IsAny>>()))//.Returns etc...;
然而,你遇到了Moq的一个缺点。 你想在那里放一个实际的表达式而不是使用It.IsAny
,但是Moq不支持设置带有特定表达式的表达式的方法(这是一个很难实现的特性)。 难点在于必须弄清楚两个表达式是否相同。
因此,在您的测试中,您可以传入任何 Expression
,它将传回您设置模拟返回的任何内容。 测试的价值有点稀释。
让.Returns调用返回表达式的结果对你的allPages变量。
_mockRepos.Setup(x => x.Single(It.IsAny>>())) .Returns( (Expression> predicate) => allPages.Where(predicate) );
我发现It.Is
应该代替It.IsAny
来获得更准确的结果。
Page expectedPage = new Page {Id = 12, Title = "Some Title"}; _mockRepos.Setup(x => x.Single(It.Is>>(u => u.Compile().Invoke(expectedPage)))) .Returns(() => expectedPage);
使用Moq的It.IsAny<>
而不使用.CallBack
强制您编写测试未涵盖的代码。 相反,它允许任何查询/表达式通过,从unit testing的角度来看,使得模拟基本没用。
解决方案:您需要使用Callback来测试表达式,或者您需要更好地约束您的模拟。 无论哪种方式都是混乱和困难的。 只要我练习TDD,我就已经解决了这个问题。 我终于把一个帮助类放在一起,使它更具表现力和更少杂乱。 以下是一个可能的最终结果的示例:
mockPeopleRepository .Setup(x => x.Find(ThatHas.AnExpressionFor() .ThatMatches(correctPerson) .And().ThatDoesNotMatch(deletedPerson) .Build())) .Returns(_expectedListOfPeople);
这是博客文章,讨论它并提供源代码: http : //awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/