所有語言的單元測試的一般規則

什麼是單元測試?

單元測試是對程式碼的測試,以確保它執行它要執行的任務。它以儘可能最低的級別測試程式碼 - 類的各個方法。

什麼是單位?

任何可以單獨測試的離散程式碼模組。大多數時間類和他們的方法。該類通常被稱為被測試類(CUT)或被測系統(SUT)

單元測試和整合測試之間的區別

單元測試是單獨測試單個類的行為,完全不同於它的任何實際依賴性。整合測試是測試單個類及其一個或多個實際依賴項的行為。

SetUp 和 TearDown

在每個單元測試之前執行 SetUp 方法,在每次測試之後執行 TearDown。

通常,你可以在 SetUp 中新增所有先決條件步驟,並在 TearDown 中新增所有清理步驟。但是,如果每個測試都需要這些步驟,那麼你只能使用這些方法。如果沒有,則在安排部分的特定測試中採取這些步驟。

如何處理依賴項

很多時候,類具有其他類的依賴性來執行其方法。為了能夠不依賴這些其他類,你必須假裝這些。你可以自己建立這些類,也可以使用隔離或模型框架。隔離框架是一組程式碼,可以輕鬆建立虛假類。

假類

任何提供足以假裝它是 CUT 所需依賴的功能的類。有兩種型別的假貨:Stubs 和 Mocks。

  • 存根:對測試的通過或失敗沒有影響的假,並且純粹為了允許測試執行而存在。
  • 模擬:一種假冒,它跟蹤 CUT 的行為,並根據該行為傳遞或未通過測試。

為什麼單元測試?

1.單元測試會發現錯誤

當你編寫一整套測試來定義給定類的預期行為時,會顯示任何不符合預期的行為。

單元測試可以防止錯誤

進行一項引入錯誤的更改,你的測試可以在下次執行測試時將其顯示出來。

3.單元測試可節省時間

編寫單元測試有助於確保你的程式碼從一開始就按設計工作。單元測試定義了程式碼應該做什麼,因此你不會花時間編寫執行它不應該執行的操作的程式碼。沒有人檢查他們不相信有效的程式碼,你必須做一些事情讓自己認為它有效。花時間編寫單元測試。

4.單元測試讓你高枕無憂

你可以執行所有這些測試,並知道你的程式碼按預期工作。瞭解程式碼的狀態,它的工作原理,以及你可以毫無顧慮地更新和改進它是一件非常好的事情。

5.單元測試證明正確使用類

單元測試成為程式碼如何工作,預期執行的操作以及使用正在測試的程式碼的正確方法的簡單示例。

單元測試的一般規則

1.對於單元測試的結構,請遵循 AAA 規則

安排:

設定要測試的東西。像變數,欄位和屬性一樣,可以執行測試以及預期的結果。

行為: 實際上呼叫你正在測試的方法

斷言:

呼叫測試框架以驗證你的行為的結果是否符合預期。

2.單獨測試一件事

所有類都應該單獨測試。它們不應該依賴於模擬和存根以外的任何東西。他們不應該依賴於其他測試的結果。

3.首先寫下簡單的中間測試

你編寫的第一個測試應該是最簡單的測試。它們應該是基本上可以輕鬆地說明你要編寫的功能的那些。然後,一旦這些測試通過,你應該開始編寫更復雜的測試來測試程式碼的邊緣和邊界。

4.編寫測試邊緣的測試

一旦測試了基礎知識並且你知道基本功能有效,就應該測試邊緣。一組好的測試將探索給定方法可能發生的事情的外邊緣。

例如:

  • 如果發生溢位會發生什麼?
  • 如果值變為零或更低,該怎麼辦?
  • 如果他們去 MaxInt 或 MinInt 怎麼辦?
  • 如果你建立一個 361 度的圓弧怎麼辦?
  • 如果傳遞空字串會發生什麼?
  • 如果字串大小為 2GB 會發生什麼?

5.跨越邊界測試

單元測試應測試給定邊界的兩側。跨越邊界是程式碼可能失敗或以不可預測的方式執行的地方。

6.如果可以,測試整個光譜

如果它是實用的,請測試你的功能的整個可能性。如果它涉及列舉型別,請使用列舉中的每個項測試功能。測試每種可能性可能是不切實際的,但如果你能測試每種可能性,那就去做吧。

7.如果可能,請覆蓋每個程式碼路徑

這個也很有挑戰性,但如果你的程式碼是為測試而設計的,並且你使用了程式碼覆蓋工具,那麼你可以確保程式碼的每一行都至少被單元測試覆蓋一次。覆蓋每個程式碼路徑並不能保證沒有任何錯誤,但它肯定會為你提供有關每行程式碼狀態的寶貴資訊。

8.編寫揭示錯誤的測試,然後修復它

如果你發現了錯誤,請編寫一個顯示錯誤的測試。然後,你可以通過除錯測試輕鬆修復錯誤。然後你有一個很好的迴歸測試,以確保如果 bug 因任何原因回來,你馬上就會知道。當你在偵錯程式中執行簡單,直接的測試時,修復錯誤非常容易。

這方面的一個好處是你已經測試了你的測試。因為你已經看到測試失敗,然後當你看到它通過時,你知道該測試是有效的,因為它已被證明可以正常工作。這使它成為更好的迴歸測試。

9.使每個測試彼此獨立

測試不應該相互依賴。如果你的測試必須按特定順序執行,則需要更改測試。

10.每次測試寫一個斷言

你應該為每個測試編寫一個斷言。如果你不能這樣做,那麼請對程式碼進行折射,以便使用 SetUp 和 TearDown 事件正確建立環境,以便可以單獨執行每個測試。

11.清楚地命名你的測試。不要害怕長名

由於每個測試都在執行一個斷言,因此每個測試最終都非常具體。因此,不要害怕使用長而完整的測試名稱。

一個完整的長名稱可以讓你立即知道測試失敗的原因以及測試嘗試的確切內容。

長,明確命名的測試也可以記錄你的測試。名為 DividedByZeroShouldThrowException 的測試準確記錄了當你嘗試除以零時程式碼所執行的操作。

12.測試實際引發的每個引發的異常

如果你的程式碼引發了異常,那麼編寫一個測試來確保你實際引發的每個異常都應該被引發。

13.避免使用 CheckTrue 或 Assert.IsTrue

避免檢查布林條件。例如,如果檢查兩個東西是否與 CheckTrue 或 Assert.IsTrue 相等,請改用 CheckEquals 或 Assert.IsEqual。為什麼?因為這:

CheckTrue(預期,實際)這將報告類似:“某些測試失敗:預期為 True 但實際結果為 False。”

這並沒有告訴你任何事情。

CheckEquals(預期,實際)

這將告訴你類似的事情:“一些測試失敗:預期 7 但實際結果為 3”。

當你的預期值實際上是布林條件時,僅使用 CheckTrue 或 Assert.IsTrue。

14.不斷執行測試

在編寫程式碼時執行測試。你的測試應該快速執行,使你能夠在經過微小更改後執行它們。如果你無法在正常開發過程中執行測試,那麼就會出現問題。單元測試應該幾乎立即執行。如果不是,那可能是因為你沒有孤立地執行它們。

15.將測試作為每個自動構建的一部分執行

正如你在開發過程中應該執行測試一樣,它們也應該是你持續整合過程中不可或缺的一部分。測試失敗應該意味著你的構建被破壞了。不要讓失敗的測試徘徊。將其視為構建失敗並立即修復。