Moq.Mock <t> - 如何设置采用表达式的方法</t>

时间:2010-05-01 23:36:22

标签: c# unit-testing nunit moq

我正在模拟我的存储库界面,我不知道如何设置一个接受表达式并返回一个对象的方法?我正在使用Moq和NUnit

接口:

public interface IReadOnlyRepository : IDisposable
{
    IQueryable<T> All<T>() where T : class;
    T Single<T>(Expression<Func<T, bool>> expression) where T : class;
}

使用IQueryable进行测试已设置,但不知道如何设置T Single:

private Moq.Mock<IReadOnlyRepository> _mockRepos;
private AdminController _controller;
[SetUp]
public void SetUp()
{
    var allPages = new List<Page>();
    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<IReadOnlyRepository>();
    _mockRepos.Setup(x => x.All<Page>()).Returns(allPages.AsQueryable());
    //Not sure what to do here???
    _mockRepos.Setup(x => x.Single<Page>()
    //----
    _controller = new AdminController(_mockRepos.Object);
}

4 个答案:

答案 0 :(得分:40)

您可以这样设置:

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))//.Returns etc...;

但是你遇到了Moq的一个缺点。你可能希望在那里放置一个实际的表达式,而不是使用It.IsAny,但是Moq不支持设置带有特定表达式的表达式的方法(这是一个很难实现的特性)。难点在于必须弄清楚两个表达式是否相同。

因此,在您的测试中,您可以传入任何 Expression<Func<Page,bool>>,它会传回您设置模拟的任何内容。测试的价值有点稀释。

答案 1 :(得分:7)

让.Returns调用对你的allPages变量返回表达式的结果。

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))
    .Returns( (Expression<Func<Page, bool>> predicate) => allPages.Where(predicate) );

答案 2 :(得分:4)

我发现应使用It.Is<T>代替It.IsAny<T>以获得更准确的结果。

Page expectedPage = new Page {Id = 12, Title = "Some Title"};
_mockRepos.Setup(x => x.Single<Page>(It.Is<Expression<Func<Page, bool>>>(u => u.Compile().Invoke(expectedPage))))
                       .Returns(() => expectedPage);

答案 3 :(得分:1)

在没有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/