2011-12-15 35 views
2

我试图模拟出一个数据业务背景下,作为这部分我有这需要在Moq.Mock <T>建立表达式为模拟使用最小起订量导致模拟设置不匹配

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

当我尝试嘲笑这种方法了,起订量总是返回上模拟一个

所有调用必须有相应的设置。 TearDown中:Moq.MockException:下面设置不匹配: IContext米=> 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); 
    } 
} 

回答

5

我相信这是下降到这个在你的设置...

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

这并不参数数组匹配。尝试...

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

OMG!非常感谢!!我不相信我忽略了这一点!它现在工作耶:-) – 2011-12-15 02:54:52

1

你正在设置你的模拟,你已经从它派生出一个实例。一旦你获得了一个对象,这将不会受到对模拟的任何改变的影响。例如:

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 

一个dity的方式来解决你的代码是从安装方法删除实例化的语句,使您的控制器懒惰,如:

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

这样一来,只要你消耗你的控制器在第一次安装后,您将始终使用最新版本。

1

使用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/