Moq.Mock< T>使用MOQ在Mock中设置表达式会导致模拟设置不匹配

本文关键字:设置 Mock 表达式 模拟 不匹配 使用 Moq MOQ | 更新日期: 2023-09-27 18:17:59

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

  • 一个表达式(谓词),
  • 可选字符串参数
  • 一个参数与一个数组的谓词。

当我尝试模拟这个方法时,MOQ总是返回一个

模拟上的所有调用必须具有相应的设置。拆卸: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);
    }
}

Moq.Mock< T>使用MOQ在Mock中设置表达式会导致模拟设置不匹配

我相信这是在你的设置…

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

不匹配params数组。试一试…

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

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

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语句,并使控制器变为lazy,如:

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

这样,只要你在第一次安装后使用你的控制器,你将总是使用最新的版本。

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

解决方案:您要么需要使用Callback来测试表达式,要么需要更好地约束mock。无论哪种方式都是混乱和困难的。自从我开始练习TDD以来,我就一直在处理这个问题。最后,我拼凑了一个helper类,使它更有表现力,更简洁。下面是一个可能的最终结果:

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/