2016-04-15 60 views
3

这里是我遇到问题的简化版本:安装在模拟没有返回预期值

public interface IService 
{ 
    IProvider Provider { get; } 
} 

public interface IProvider 
{ 
    List<int> Numbers{ get; } 
    string Text { get; } 
} 

[TestMethod] 
public void ServiceTest() 
{ 
    var service = new Mock<IService>(); 
    var provider = new Mock<IProvider>(); 

    service.Setup(s => s.Provider).Returns(provider.Object); // A 
    service.Setup(s => s.Provider.Text).Returns("some text"); // B - incorrect 

    // they actually meant to do this, instead of 'B' 
    // provider.Setup(p => p.Text).Returns("some text"); 

    provider.Setup(p => p.Numbers).Returns(new List<int> { 1, 2, 3 }); 

    DummyApplicationCode(service.Object); 
} 

int DummyApplicationCode(IService service) 
{ 
    // will throw, because the Provider was replaced at 'B' 
    int shouldBeOne = service.Provider.Numbers.First(); 
    return shouldBeOne; 
} 

一个单元测试失败,因为这样在测试中的应用程序代码下来,嘲笑IService返航错IProvider

我最终找到行(记住我一直在寻找的代码是不是如上一样简单),这造成了它,标记为上面的“B”,其别人都因误会起订量设置添加。

我知道后面的模拟设置将覆盖以前的设置但我没有发现这个问题,因为违规行的返回是针对单独的子属性。

我期望这是通过设计,但它扔了我,因为我没有预料到有人会这样做。

我的问题:因为在“B”的设置是只关注供应商文本,为什么服务“提供者”属性需要更换这是在“A”定义的回报?

+0

'如果为相同的方法或属性指定了多个设置,则最新的设置获胜并且将执行该设置。 'Provider'是你的情况下正在执行的表达式树的一部分。因此,涉及该路径的任何以前的设置也将被覆盖。 – Nkosi

+0

@Nkosi这是公平的,谢谢,但我的查询是为什么它真的需要**来做到这一点,而不是将文本返回到现有的提供者模拟。就我个人而言,我希望看到该行被标记为错误。 – mungflesh

+2

我明白你的观点。我的猜测是每个'安装程序'都被孤立地处理。一个独特的动作/函数被调用。你可以在GitHub上为它创建一个功能请求的问题。 – Nkosi

回答

1

此查看源时,显然是故意的:

https://github.com/moq/moq4/blob/master/Source/Mock.cs

https://github.com/moq/moq4/blob/master/Source/Interceptor.cs

Setup创建一个 “呼叫” 通过Interceptor使用AddCall。这包含以下代码块,只要我们正在创建非条件设置,就会删除以前的所有设置。它甚至评论。

if (!call.IsConditional) 
      { 
       lock (calls) 
       { 
        // if it's not a conditional call, we do 
        // all the override setups. 
        // TODO maybe add the conditionals to other 
        // record like calls to be user friendly and display 
        // somethig like: non of this calls were performed. 
        if (calls.ContainsKey(key)) 
        { 
         // Remove previous from ordered calls 
         InterceptionContext.RemoveOrderedCall(calls[key]); 
        } 

        calls[key] = call; 
}