我們在撰寫商業邏輯時,能夠準確預期功能在什麼情況會產生例外也是一門學問;因此,在此段會探討驗證例外的寫法與歷史(來源可參考單元測試的藝術 2.6.2 節或 Marcus 文章的 NUnit 測試例外 Exception)。
在探討驗證例外之前,我們先再回顧 HelloBank 這隻商業邏輯專案的內容—存款功能,如下:
public void Add(double amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount));
}
balance += amount;
}
從此段程式碼,可以看出來當使用者存款的金額小於 0 元的時候,會發生 ArgumentOutOfRangeException。而在早期 NUnit 2 的時代,是採用一種寫法叫 [ExpectedExcetption] 的寫法,如下:
[Test]
[ExpectedExcetption(typeof(ArgumentOutOfRangeException))]
public void Adding_Negative_Funds_Throws()
{
// Act
account.Add(-500);
}
但在單元測試的藝術有提到該方法並非良好的方法,其原因為 [ExpectedExcetption] 的概念是告訴測試執行器,將這個功能放進 try-catch 的區塊中,找出該段程式碼的 Exception。這樣的風險可能會造成抓取出來的例外可能不是我們預期的例外(比如建構函式寫錯,拋出例外時造成測試未成功,進而導致誤判程式碼出錯原因)。另外,NUnit 3 已將此寫法刪除(換言之,這種寫法已經走入歷史了)。
在單元測試的藝術提供的寫法是利用 Assert.Catch(delegate),該方法會回傳例外的物件執行個體,進而對該物體驗證其正確性(比如抓取其中的 Message,並驗證是否有涵蓋我們預期的字串)。但在 NUnit 3 有提供另一種寫法,可以更簡潔地解決驗證例外,程式碼如下:
[Test]
public void Adding_Negative_Funds_Throws()
{
// Act + Assert
Assert.Throws<ArgumentOutOfRangeException>(() => account.Add(-500));
}
程式碼採用 Assert.Throws(delegate) 寫法,我們把原先 Act 的動作利用 Lambda 語法寫進委派中,所以可看到 Act 與 Assert 的程式碼寫在同一行,利用 Assert.Throws 驗證 Lambda 內的委派結果與泛型是同一種例外類別。
PS:若對 C# 不懂,T 是指泛型,而 delegate 是指委派(在此不解釋各自的細節,可參考能不能講一下什麼是泛型(Generics)與老宅筆記本: .NET委派(delegate),寫得滿淺顯易懂的)。
該特性如標題,顧名思義就是指把測試指定你分類的名稱,程式碼如下:
[Test]
[Category("Exception Tests")]
public void Adding_Negative_Funds_Throws()
{
// Act + Assert
Assert.Throws<ArgumentOutOfRangeException>(() => account.Add(-500));
}
而透過 Windows Test Explorer 可看到做分類的特性,如圖:
PS:尚未在 Visual Studio for Mac 看到相關的 UI 可以顯性特性(也可能是我沒找到),以及如何透過 Category 只跑相對應的測試,也會在之後花時間 Study 並補齊。