2016-04-15 26 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; 
}