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/