Moq.Mock使用最小起订量将表达式设置到模拟中会导致模拟设置不匹配
我试图模拟一个数据服务上下文,作为其中的一部分,我有一个方法,它接受
- 一个表达式(谓词)、
- 一个可选的字符串参数
- 和一个带有谓词数组的参数。
当我尝试模拟此方法时,最小起订量总是返回一个
模拟上的所有调用都必须有相应的设置。 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我相信这取决于您的设置...
与 params 数组不匹配。尝试...
I believe it is down to this in your Setup...
Which does not match the params array. Try...
在从中派生出一个实例后,您正在设置您的模拟。一旦你获得了一个对象,它就不会受到任何模拟更改的影响。示例:
修复代码的一个重要方法是从 Setup 方法中删除 instantiate 语句并使您的 Controller 变得懒惰,例如:
这样,只要您在第一次设置后使用控制器,您将始终使用最新版本。
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:
a dity way to fix your code is to remove the instantiate statement from the Setup method and make your Controller lazy, like:
This way, as long as you consume your controller after setup for the first time, you will always work with the latest version.
使用 Moq 的
It.IsAny<>
而不使用.CallBack
会迫使您编写测试未涵盖的代码。相反,它允许任何查询/表达式通过,从单元测试的角度来看,你的模拟基本上毫无用处。解决方案:您要么需要使用回调来测试表达式,要么需要更好地约束您的模拟。无论哪种方式都是混乱且困难的。自从我实践 TDD 以来,我就一直在处理这个问题。我最终组合了一个辅助类,使其更具表现力且不那么混乱。这是一个可能的最终结果:
这是一篇讨论它并提供源代码的博客文章:
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:
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/