2012-10-24 80 views
2

我在Visual Studio 2012中使用最新版本的NUnit(2.6.2),使用了resharper和visual studio測試運行器。我有以下示例測試,其中我試圖驗證在預期的異步方法調用中引發異常。異步單元測試不能按預期工作

不幸的是,這似乎沒有按預期工作。第一個測試AsyncTaskCanceledSemiWorking僅適用,因爲我有expectedexception屬性。實際的斷言是完全被忽略的(正如你可以通過ArgumentOutOfRange異常看到的那樣,這只是一個假的讓它失敗)。

AsyncTaskCanceledWorking工作正常,但沒有測試在指定的行上引發異常,因此不太有用。

第三與下面莊嚴失敗....

System.Threading.Tasks.TaskCanceledException : A task was canceled. 
Exception doesn't have a stacktrace 

我如何從一個特定的線路測試的TaskCanceledException將是非常有用的任何想法。

感謝

[Test] 
    [ExpectedException(typeof(TaskCanceledException))] 
    public async Task AsyncTaskCanceledSemiWorking() 
    { 
     CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
     CancellationToken token = cancellationTokenSource.Token; 

     cancellationTokenSource.Cancel(); 

     Assert.That(await LongRunningFunction(token), Throws.InstanceOf<ArgumentOutOfRangeException>()); 


    } 

    [Test] 
    [ExpectedException(typeof(TaskCanceledException))] 
    public async Task AsyncTaskCanceledWorking() 
    { 
     CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
     CancellationToken token = cancellationTokenSource.Token; 

     cancellationTokenSource.Cancel(); 

     int i = await LongRunningFunction(token); 
    } 


    [Test] 
    public async Task AsyncTaskCanceledFailed() 
    { 
     CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
     CancellationToken token = cancellationTokenSource.Token; 

     cancellationTokenSource.Cancel(); 

     Assert.That(await LongRunningFunction(token), Throws.InstanceOf<TaskCanceledException>()); 

    } 

    public async Task<int> LongRunningFunction(CancellationToken token) 
    { 
     token.ThrowIfCancellationRequested(); 

     await Task.Delay(1000, token); 

     return 5; 
    } 
+0

嗨,彼得,這將有助於知道你會期望什麼行爲,而不是。 – Simone

回答

10

我假設你要檢查LongRunningFunction將拋出一個TaskCanceledException

我認爲您所遇到的行爲是完全正確的,誤會是在此聲明:

await LongRunningFunction(token) 

在這裏,你得到有效執行異步操作,並等待它完成,這也將重新拋出調用中發生的第一個異常。你基本上可以替換成:

throw new TaskCanceledException() 

因此,爲什麼前兩個測試是成功的 - 您使用的是ExpectedExceptionAttribute - 第三失敗 - 你沒有預期的異常。

當你以後使用Throws時,Assert.That的第一個參數應該是某種類型的委託,因爲NUnit必須調用它才能捕獲從調用中冒出的異常。如果您自己調用它,除了使用ExpectedExceptionAttribute之外,NUnit當然無法捕獲異常。

換句話說,做了非常正確的做法是:

// WARNING: this code does not work in NUnit <= 2.6.2 
Assert.That(async() => await LongRunningFunction(token), Throws.InstanceOf<TaskCanceledException>()); 

我想告訴你,NUnit的支持此語法爲異步方法,這將是很自然的,並允許您測試代碼的特定部分中的例外情況,但它不會,並且測試會報告您希望發生異常但未發生異常。

原因是,爲了從異步匿名方法的調用中獲得異常,NUnit將不得不等待它,它目前沒有。

一個替代方案,我可以給你是使用非異步拉姆達,你Wait由異步操作返回的任務,但語法是不幸的是沒有好的,因爲等待異步操作在不同的行爲從方式等待它返回的任務。特別是,在異步操作引發異常的情況下,您會在第一種情況下得到實際的異常,而在第二種情況下會得到實際的異常。在任何情況下,這裏的一些代碼,將與2.6.2工作:

var aggregate = Assert.Throws<AggregateException>(() => LongRunningFunction(token).Wait()); 
Assert.IsInstanceOf<TaskCanceledException>(aggregate.InnerExceptions.Single()); 

綜上所述,雖然NUnit的2.6.2確實引入了異步測試方法的支持,它允許你寫async [void|Task|Task<T>]測試中,我們沒有考慮將支持擴展到異步匿名方法,儘管我相信我們可以這樣做,但這對於這種斷言很有用。

+0

我想等待一個拋出異常的方法,並聲明拋出異常。我相信這就是你說我實際上需要一個委託的地方。你認爲NUnit很快就能支持這一點嗎?感謝您的回覆 – Peter

+0

我無法確定地告訴您,我只能說這是可能的。請允許我指出你的第一個陳述是矛盾的。如果您等待某個操作,則無法斷言它會引發異常,除非您使用可以捕獲該異常的代碼封裝該調用,否則該異常就會冒泡,這就是您的示例中發生的情況。 – Simone

-2

彼得不幸,這似乎沒有按預期工作。第一次測試AsyncTaskCanceledSemiWorking只能工作,因爲我有expectedexception屬性。實際的斷言是完全被忽略的(正如你可以通過ArgumentOutOfRange異常看到的那樣,這只是一個假的讓它失敗)。

期望是錯誤的,因爲該屬性優先於Assert.Throws。正如Simone所示,期望是「斷言」該異常是一個InstanceOf,因此它屬於Assertion而不是拋出。我認爲你的期望可能是基於Java中使用Throws的方式。

+0

Andrew,你是什麼意思,編輯我這樣的評論? –

+0

安德魯,請不要干涉適度的名稱,因爲您完全誤導了帖子。 –

+0

安德魯,它似乎被瀏覽器吃掉的HTML標籤用法(「小於」和「大於」)讓我認爲它是由您編輯的。對於先前的評論感到抱歉(試圖刪除我的評論,但沒有規定)。 –

0

如果您在NUnit 2.6.2或更高版本中使用Resharper 7.1或更高版本,則可以使用public async void的測試方法。 Resharper 7.1今天發佈(2012年11月13日)

早期版本的Resharper測試運行器不會等待測試完成,並且可以在沒有實際測試的情況下通過測試。

2.6.2之前的NUnit版本有很多相同的問題。

Resharper NUnit測試運行器與NUnit GUI和命令行測試運行器不是相同的代碼庫,並且由Jetbrains單獨更新。