2011-04-20 51 views
1

我的團隊在C#項目中廣泛使用NUnit單元測試。最近我們已經開始在.NET 4中使用任務並行庫(TPL),它已經爲我們引入了一個皺紋。如何檢測nunit測試套件中的UnobservedTaskException錯誤

在TPL中,如果某個任務出現故障(即執行該任務時引發了異常),則該任務的Exception屬性必須至少檢索一次。如果不是,當Task對象由垃圾回收器完成時,會拋出異常,終止進程。

可以通過註冊TaskScheduler.UnobservedTaskException的處理程序來檢測和防止這種情況。我們已經爲我們的一些測試用例重現了未觀察到的任務異常錯誤,但我寧願有一些方法來修改NUnit運行測試的方式,以便爲每個測試註冊一個UnobservedTaskException處理程序,然後在測試之後垃圾收集被迫清除任何沒有被觀察到的異常的任務。那麼我會喜歡這樣做,導致測試失敗。

其他團隊如何解決這個問題?你如何檢測通過的測試用例(即完成沒有任何異常),但留下一個或多個任務對象沒有被觀察到的異常?

回答

2

這是我如何做到這一點:

[Test] 
public void ObservesTaskException() 
{ 
    bool wasUnobservedException = false; 

    TaskScheduler.UnobservedTaskException += 
     (s, args) => wasUnobservedException = true; 

    CauseATaskToThrowInTheSystemUnderTest(); 

    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    Assert.That(wasUnobservedException, Is.False); 
} 

到CauseATaskToThrowInTheSystemUnderTest()的調用是任何你需要做的佔位符。我建議將代碼包裝在這樣的函數中,因爲它有助於確保在GC運行時引發異常的Task對象無法訪問。如果任務對象可達,終結器將不會運行,並且此測試不會測試任何內容。

顯然,在垃圾收集發生之前確保任務已完成也很重要。你如何做到這一點(如果你可以的話)取決於你的特定代碼。也許你的程序流程確保了這種情況。如果不是這樣,如果你的測試可以訪問任務的對象,你可以做到以下幾點:

var continuation = GetTheTaskFromTheSystemUnderTest(); 
    .ContinueWith(t => {}); 

CauseATaskToThrowInTheSystemUnderTest(); 

bool isTaskCompleted = continuation.Wait(SomeSuitableTimeout); 

您應該添加isTaskCompleted把斷言。

超時時間是爲了防止將來代碼被破壞 - 您不希望測試掛起。該值應該非常小。如果您發現實際上需要等待很長時間才能完成任務,那麼您的測試套件對於頻繁使用可能會太慢。

有權訪問您創建的任務(包括您在其他任務上創建的延續任務)是設計可測試性時需要考慮的一個注意事項。這樣做時,通常的折衷適用。我嘗試在我的代碼創建任務時包含這樣的測試 - 這是我的代碼的一個重要行爲,需要進行測試。

+0

我最終自己也得到了同樣的答案。很棒。在調用GC.Collect和GC.WaitForPendingFinalizers之前,有一些可能會完成任務,但在實踐中,我發現可能性太低以至於無所謂。 – anelson 2011-08-06 04:10:54

+0

你確定這可以嗎?我只是嘗試了'Task.Run(Throw)'和'Task.Factory.StartNew(Throw)'在一個'int Throw(){throw new Exception(); },在任務上嘗試了一個'use()'範圍,嘗試了連續任務,並且根本沒有任何任務變量。即使嘗試使用Thread.Sleep()或使用ManualResetEvent確保任務完成並給系統一些時間來完成任務,該事件也不會引發給我。 – angularsen 2016-02-02 12:23:38

+0

重複問題: http://stackoverflow.com/questions/21266137/test-for-unobserved-exceptions – angularsen 2016-02-02 12:30:54

0

我會保持單元測試的確定性,然後用CHESS或Jinx做功能測試。

這意味着我沒有代碼依賴於單元測試邏輯中的併發流。至於測試多個異常打包/解包,我會測試它,就好像它是一個循環:沒有例外,一個例外和兩個例外證明整個事情。

+0

夠公平的;你如何解決CHESS或Jinx中未被發現的例外問題? – anelson 2011-04-21 23:22:43

+0

看來你正在測試TPL的核心功能。也就是說,你正在檢查它是否仍然組裝一些未處理的異常,並將它們傳遞給正確的啓動線程。您似乎很清楚每個線程上拋出的異常。我會單元測試線程上的邏輯,並依靠dotNET仍然適用於線程的事實。 – GregC 2011-04-22 03:53:11

+0

斷言一段代碼拋出Assert.Throws ()的某個異常。 CHESS和Jinx可以以各種交錯方式運行您的代碼,並查看您的單元測試是否仍然存在,確保共享狀態得到適當保護。 – GregC 2011-04-22 03:55:37