2010-06-22 35 views
0

我有一個單元測試,我嘲笑(與moq)一個對象,並讓它驗證它是否正確執行一個方法。此方法正在我創建的SUT(被測系統)中創建的線程中執行。當我想在Mock上執行VerifyAll()時,可能會發生線程仍在運行並且尚未執行該方法 - 未通過測試。單元測試結果,由處理線程檢索

有沒有辦法在正確的事情上解決這個問題?例如讓VerifyAll等待什麼?因爲現在,測試是不可靠的。

這是測試:

[Test] 
    public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject() 
    { 
     var subject = new Subject(); 
     var rule = new Mock<IBusinessRule>(); 
     rule.Setup(x => x.RunChildren(subject)); //RunChildren will be called in a seperate Thread 

     InBuffer.TryToExecute(subject, rule.Object); 

     rule.VerifyAll(); //It could be possible that the Thread is still running and that RunChildren() isn't invoked yet, thus failing the test. 
    } 

    public void TryToExecute(Subject subject, IBusinessRule rule){ 
     var thread = new Thread(x => 
       { 
        SetCurrentAsProcessing(subject); 
        rule.RunChildren(subject) // This is where it executes 
        RemoveFromProcess(subject); 
       }); 

     thread.Start(); // Start the Thread 
    } 
+0

請問您可以粘貼TryToExecute方法嗎? – Grzenio 2010-06-22 12:24:45

+0

當然,發佈它。不知道爲什麼你需要它,因爲問題的根源是明確的。線程仍可能正在執行,驗證將有可能失敗。 – Bas 2010-06-22 12:29:56

+0

如何檢測安裝是否在實際應用中完成? 我認爲你需要類似processComplete事件或狀態對象。 – 2010-06-22 12:35:45

回答

0

在測試中使用顯示器:

Monitor.Enter(lock) 
Monitor.Wait(lock, timeout) // Will pause here until pulsed or timed out 
Monitor.Exit(lock) 

在你的模擬使用一個回調來通知監視器(注:我的起訂量的版本使用Callback而非Do):

rule.Setup(x => x.RunChildren(subject)).Do(X => { 
    ... 
    Monitor.Enter(lock); 
    Monitor.Pulse(lock); 
    Monitor.Exit(lock); 
} 

有是這裏使用監視器的一些示例(自動化測試框架的一部分):

http://code.google.com/p/wipflash/source/browse/WiPFlash/Components/AutomationElementWrapper.cs

+0

這似乎在做伎倆:]好提及!猜測沒有其他的東西能夠以適當的方式解決這個問題 – Bas 2010-06-23 12:55:49

+0

應該提到 - 確保你在調用線程代碼之前輸入顯示器,否則它可能會先完成,你會得到超時。 – Lunivore 2010-06-23 12:57:23

1

如果你的類實現某種機制,以等待異步操作完成,那麼你應該使用一個在您的測試。

如果沒有,你可以試試這個「黑客」(未經檢驗的 - 我不知道你的嘲弄框架,我不知道如何使它運行methodCalled.Set()):

[Test] 
public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject() 
{ 
    ManualResetEvent methodCalled = new ManualResetEvent(false); 

    var subject = new Subject(); 
    var rule = new Mock<IBusinessRule>(); 
    rule.Setup(x => x.RunChildren(subject)).Do(X=>methodCalled.Set()); //RunChildren will be called in a seperate Thread 

    InBuffer.TryToExecute(subject, rule.Object); 

    Assert.IsTrue(methodCalled.WaitOne(1000), "RunChildren was not called within 1000ms"); 
} 

請注意這種方法不好也不安全,所以如果你有其他選擇,就避免這樣做。