Moq.Mock使用最小起订量将表达式设置到模拟中会导致模拟设置不匹配

发布于 2024-12-21 07:35:01 字数 4382 浏览 1 评论 0原文

我试图模拟一个数据服务上下文,作为其中的一部分,我有一个方法,它接受

  • 一个表达式(谓词)、
  • 一个可选的字符串参数
  • 和一个带有谓词数组的参数。

当我尝试模拟此方法时,最小起订量总是返回一个

模拟上的所有调用都必须有相应的设置。 TearDown:Moq.MockException:以下设置不匹配: IContext m => m.Retrieve(It.IsAny() })

接口/实现下面的代码

public interface IContext
{
    IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                string entitySetName = null,
                                params Expression<Func<T, object>>[] eagerProperties);
}

public class Context : IContext
{
    private readonly DataServiceContext _context;

    public Context(DataServiceContext context)
    {
        this._context = context;
    }

    public IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                        string entitySetName = null,
                                        params Expression<Func<T, object>>[] eagerProperties)
    {
        DataServiceQuery<T> query = _context.CreateQuery<T>(entitySetName ?? "Default");
        return eagerProperties.Aggregate(query, (current, e) => current.Expand(e.ToString())).Where(predicate);
    }
}

下面是一个调用上述上下文方法的测试类

public class Test
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
}

public class SomeController
{
    private IContext _context = new Context(
        new DataServiceContext(new Uri("http://whatever/someservice.svc/")));

    public IContext ServiceContext
    {
        get
        {
            return _context ??
                   (_context = new Context(new DataServiceContext(new Uri("http://whatever/someservice.svc/"))));
        }
        set
        {
            _context = value;
        }
    }

    public Test RetrieveSomeInformation()
    {
        IQueryable<Test> tests = _context.Retrieve<Test>
                                                (
                                                    //Param 1
                                                    t => t.Prop1 == "test" && 1 == 1,

                                                    //Param 2
                                                    "Test",

                                                    //Param 3
                                                    t => t.Prop1,
                                                    t => t.Prop2,
                                                    t => t.Prop3
                                                  );
        return tests.First();
    }
}

下面是 MOQ 失败的实际测试,并显示“模拟上的所有调用都必须有一个相应的设置。”不明白为什么设置不匹配!任何帮助将不胜感激。

[TestFixture]
public class ControllerTests
{
    public MockRepository Repository { get; set; }
    protected Mock<IContext> MockContext { get; set; }
    public SomeController Controller;

    public List<Test> Tests;
    public Test Test;

    [SetUp]
    public void SetUp()
    {
        Test = new Test { Prop1 = "1", Prop2 = "2", Prop3 = "3" };
        Tests = new List<Test> { Test };

        Repository = new MockRepository(MockBehavior.Strict);

        MockContext = Repository.Create<IContext>();

        Controller = new SomeController { ServiceContext = MockContext.Object };
    }

    [TearDown]
    public void TearDown()
    {
        Repository.VerifyAll();
    }

    [Test]
    public void DetailProgramme_Test()
    {
        MockContext.Setup(m => m.Retrieve<Test>
                            (
                                //Param 1
                                It.IsAny<Expression<Func<Test, bool>>>(),

                                //Param 2
                                It.IsAny<string>(),

                                //Param 3
                                It.IsAny<Expression<Func<Test, object>>>()
                            )
                          ).Returns(Tests.AsQueryable());

        Test info = Controller.RetrieveSomeInformation();


        //myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());
        Assert.IsTrue(info == Test);
    }
}

I am trying to mock out a data service context, as part of this I have a method which takes in

  • an expression (predicate),
  • an optional string parameter
  • a params with an array of predicates.

when I try to mock this method out, MOQ always returns a

All invocations on the mock must have a corresponding setup.
TearDown : Moq.MockException : The following setups were not matched:
IContext m => m.Retrieve(It.IsAny() })

code below of the interface/implementation

public interface IContext
{
    IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                string entitySetName = null,
                                params Expression<Func<T, object>>[] eagerProperties);
}

public class Context : IContext
{
    private readonly DataServiceContext _context;

    public Context(DataServiceContext context)
    {
        this._context = context;
    }

    public IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                        string entitySetName = null,
                                        params Expression<Func<T, object>>[] eagerProperties)
    {
        DataServiceQuery<T> query = _context.CreateQuery<T>(entitySetName ?? "Default");
        return eagerProperties.Aggregate(query, (current, e) => current.Expand(e.ToString())).Where(predicate);
    }
}

The below is a test class that calls the above context method

public class Test
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
}

public class SomeController
{
    private IContext _context = new Context(
        new DataServiceContext(new Uri("http://whatever/someservice.svc/")));

    public IContext ServiceContext
    {
        get
        {
            return _context ??
                   (_context = new Context(new DataServiceContext(new Uri("http://whatever/someservice.svc/"))));
        }
        set
        {
            _context = value;
        }
    }

    public Test RetrieveSomeInformation()
    {
        IQueryable<Test> tests = _context.Retrieve<Test>
                                                (
                                                    //Param 1
                                                    t => t.Prop1 == "test" && 1 == 1,

                                                    //Param 2
                                                    "Test",

                                                    //Param 3
                                                    t => t.Prop1,
                                                    t => t.Prop2,
                                                    t => t.Prop3
                                                  );
        return tests.First();
    }
}

Below is the actual test that MOQ fails with a "All invocations on the mock must have a corresponding setup." Can't see why on earth the setup wont be matched! any help would be appreciated.

[TestFixture]
public class ControllerTests
{
    public MockRepository Repository { get; set; }
    protected Mock<IContext> MockContext { get; set; }
    public SomeController Controller;

    public List<Test> Tests;
    public Test Test;

    [SetUp]
    public void SetUp()
    {
        Test = new Test { Prop1 = "1", Prop2 = "2", Prop3 = "3" };
        Tests = new List<Test> { Test };

        Repository = new MockRepository(MockBehavior.Strict);

        MockContext = Repository.Create<IContext>();

        Controller = new SomeController { ServiceContext = MockContext.Object };
    }

    [TearDown]
    public void TearDown()
    {
        Repository.VerifyAll();
    }

    [Test]
    public void DetailProgramme_Test()
    {
        MockContext.Setup(m => m.Retrieve<Test>
                            (
                                //Param 1
                                It.IsAny<Expression<Func<Test, bool>>>(),

                                //Param 2
                                It.IsAny<string>(),

                                //Param 3
                                It.IsAny<Expression<Func<Test, object>>>()
                            )
                          ).Returns(Tests.AsQueryable());

        Test info = Controller.RetrieveSomeInformation();


        //myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());
        Assert.IsTrue(info == Test);
    }
}

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

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

发布评论

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

评论(3

蓝海 2024-12-28 07:35:01

我相信这取决于您的设置...

//Param 3
It.IsAny<Expression<Func<Test, object>>>()

与 params 数组不匹配。尝试...

//Param 3
It.IsAny<Expression<Func<Test, object>>[]>()

I believe it is down to this in your Setup...

//Param 3
It.IsAny<Expression<Func<Test, object>>>()

Which does not match the params array. Try...

//Param 3
It.IsAny<Expression<Func<Test, object>>[]>()
执手闯天涯 2024-12-28 07:35:01

在从中派生出一个实例后,您正在设置您的模拟。一旦你获得了一个对象,它就不会受到任何模拟更改的影响。示例:

object instance = mock.Object; // this version wont include what you have configured in the setup
mock.Setup(...);
object instance2 = mock.Object;  // get the latest version including whatever you have configured in the setup

修复代码的一个重要方法是从 Setup 方法中删除 instantiate 语句并使您的 Controller 变得懒惰,例如:

public SomeController Controller = new Lazy<SomeController>(() => new SomeController() { ServiceContext = MockContext.Object });

这样,只要您在第一次设置后使用控制器,您将始终使用最新版本。

You are setting up your Mock after you have derived an instance out of it. Once you gained an object, this will not be affected by any changes to the mock. example:

object instance = mock.Object; // this version wont include what you have configured in the setup
mock.Setup(...);
object instance2 = mock.Object;  // get the latest version including whatever you have configured in the setup

a dity way to fix your code is to remove the instantiate statement from the Setup method and make your Controller lazy, like:

public SomeController Controller = new Lazy<SomeController>(() => new SomeController() { ServiceContext = MockContext.Object });

This way, as long as you consume your controller after setup for the first time, you will always work with the latest version.

吹梦到西洲 2024-12-28 07:35:01

使用 Moq 的 It.IsAny<> 而不使用 .CallBack 会迫使您编写测试未涵盖的代码。相反,它允许任何查询/表达式通过,从单元测试的角度来看,你的模拟基本上毫无用处。

解决方案:您要么需要使用回调来测试表达式,要么需要更好地约束您的模拟。无论哪种方式都是混乱且困难的。自从我实践 TDD 以来,我就一直在处理这个问题。我最终组合了一个辅助类,使其更具表现力且不那么混乱。这是一个可能的最终结果:

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

这是一篇讨论它并提供源代码的博客文章:

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 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 和您的相关数据。
原文