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

发布于 2024-08-31 04:26:47 字数 1032 浏览 3 评论 0 原文

我正在模拟我的存储库接口,并且不确定如何设置一个接受表达式并返回对象的方法?我正在使用 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);
}

I am Mocking my repository interface and am not sure how to set up a method that takes an expression and returns an object? I am using Moq and NUnit.

Interface:

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

Test with IQueryable is already set up, but don't know how to set up the 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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

十秒萌定你 2024-09-07 04:26:47

您可以这样设置:

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

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

因此,在您的测试中,您可以传入 any Expression> ,它将传回您设置的模拟要返回的任何内容。测试的价值被稍微稀释了。

You can set it up like this:

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

However you are coming up against one of Moq's shortcomings. You would want to put an actual expression there instead of using It.IsAny, but Moq doesn't support setting up methods that take expressions with specific expressions (it's a difficult feature to implement). The difficulty comes from having to figure out whether two expressions are equivalent.

So in your test you can pass in any Expression<Func<Page,bool>> and it will pass back whatever you have setup the mock to return. The value of the test is a little diluted.

物价感观 2024-09-07 04:26:47

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

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

Have the .Returns call return the result of the expression against your allPages variable.

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))
    .Returns( (Expression<Func<Page, bool>> predicate) => allPages.Where(predicate) );
转瞬即逝 2024-09-07 04:26:47

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

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);

I have found that It.Is<T> should be used in place of It.IsAny<T> for more accurate results.

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);
把昨日还给我 2024-09-07 04:26:47

使用 Moq 的 It.IsAny<> 而不使用 .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/

Using Moq's It.IsAny<> without a .CallBack forces you to write code that's not covered by your test. Instead, it allows any query/expression at all to pass through, rendering your mock basically useless from a unit testing perspective.

The solution: You either need to use a Callback to test the expression OR you need to constrain your mock better. Either way is messy and difficult. I've dealt with this issue for as long as I've been practicing TDD. I finally threw together a helper class to make this a lot more expressive and less messy. Here's an example of one possible end-result:

mockPeopleRepository
  .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
    .ThatMatches(correctPerson)
    .And().ThatDoesNotMatch(deletedPerson)
    .Build()))
  .Returns(_expectedListOfPeople); 

Here's the blog article that talks about it and gives the source code: http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文