2012-11-01 47 views
0

今天我遇到了一個奇怪的問題 - 我正在圍繞SIM對象編寫單元測試。它斷言當SIM對象被更新並且PIN嘗試保持改變時,它將調用特定的方法。該測試是這樣的:Moq在構造函數中使用延遲加載

[Test] 
public void TestUpdateSimInfoWithPinAttemptsChangedCallsOnPinAttemptsRemaining() 
{ 
    var info = new SimPinInfo {PinAttemptsRemaining = 10}; 

    var sim = new Mock<Sim>(info); 

    info.PinAttemptsRemaining = 2; 

    sim.Object.UpdateSimInfo(info); 

    sim.Verify(s => s.FireOnPinAttemptsRemaining(), Times.Once()); 
} 

所以模擬SIM對象與10次PIN嘗試其餘創建。在傳遞給UpdateSimInfo()方法之前,SimPinInfo對象將PinAttemptsRemaining值減爲2。

SIM構造(修剪清晰度):

internal Sim(SimPinInfo info) : this() 
{ 
    _pinAttemptsRemaining = info.PinAttemptsRemaining; 
    _pukAttemptsRemaining = info.PukAttemptsRemaining; 
    ...... 
} 

而且UpdateSimInfo()法(修整):

internal void UpdateSimInfo(SimPinInfo info) 
{ 
    lock(_locker) 
    { 
     if (_pinAttemptsRemaining != info.PinAttemptsRemaining) 
     { 
      Log("PinAttemptsRemaining changed"); 
      _pinAttemptsRemaining = info.PinAttemptsRemaining; 
      FireOnPinAttemptsRemaining(); 
     } 
     ..... 
    } 
} 

一個非常簡單的測試 - 會發生什麼是上面的if語句爲真(引腳嘗試剩餘已更改),因此OnPinAttemptsRemaining事件將被觸發。然而,測試失敗了(儘管不是所有的時間 - 當我慢慢地通過代碼時它通過了!)。什麼是發生在if語句是假的 - 既_pinAttemptsRemaininginfo.PinAttemptsRemaining是2.看來,預計當SIM模擬不實際創建 - 當info.PinAttemptsRemaining是10

爲了證明這一點,我添加了一個評論:

var sim = new Mock<Sim>(info); 
info.PinAttemptsRemaining = 2; 
Console.WriteLine("SIM's pin attempts = " + sim.Object.PinAttemptsRemaining); 

我也在SIM對象的構造函數中放置了一個斷點。跳過Console.WriteLine行時,跳過了斷點,而不是new Mock...行。所以這個對象直到需要時纔會被創建。

我相信這被稱爲延遲加載或懶惰評估。

有這種行爲的各種解決方法 - 我結束了創建一個新的SimPinInfo對象傳遞到UpdateSimInfo()

以前有沒有人遇到過這種行爲?我找不到任何參考。

回答

2

從我讀過的你試圖測試一個模擬。

sim.Object.UpdateSimInfo(info); 

嘲笑是爲了取代依賴關係,你不用它們來代替你的意思測試代碼。沒有看到整個代碼,我猜測你甚至不需要一個模擬來測試這種行爲。假設FireOnPinAttemptsRemaining引發事件我建議像這樣的測試方法:

[Test] 
public void TestUpdateSimInfoWithPinAttemptsChangedCallsOnPinAttemptsRemaining() 
{ 
    int eventFiredCount = 0; 
    var info = new SimPinInfo {PinAttemptsRemaining = 10}; 
    var sim = Sim(info); 
    sim.OnPinAttemptsRemaining += (sender, e) => { eventFiredCount++; }; 

    info.PinAttemptsRemaining = 2; 
    sim.UpdateSimInfo(info); 

    Assert.AreEqual(1, eventFiredCount); 
} 

我不是在事件處理程序100%,因爲我不知道消防法引發一個事件或不做或事件名稱,但它應該給你一個想法。

+0

謝謝史蒂夫。我開始使用監視器等實際上等待不同線程上的事件的單元測試。下一步看起來像你的代碼,它運行良好,我終於在我的問題中使用了模擬代碼。我可能會回到你的風格,但你可以解釋「替代依賴/替代代碼」的評論。乾杯。 – barry

+0

可能是最簡單的解釋方法。假設你有一個包裝數據訪問的服務。 DataService的。它直接與您的數據庫進行通話。您有其他使用DataService檢索和保存信息的代碼。現在您要測試一些需要從DataService獲取數據的代碼。 Mock允許您通過嘲笑數據服務來獨立於數據服務來測試代碼。例如,我將爲數據服務提供稱爲IDataService的合同接口。通過使用Mock ,您可以設置預期的場景,而不需要「真正的」數據連接。 –