章節概述#

本章是 Part 2 的回顧與總結。作者反思了自行實作 xUnit 的價值、xUnit 在實際使用中的一些重要區分(assertion failure vs. error),以及如何將 xUnit 擴展到不同語言和場景。

自行實作 xUnit 的理由#

xUnit 已被移植到超過 30 種程式語言,你使用的語言很可能已經有現成的實作。但作者認為仍有兩個理由值得自己動手做一次:

  • 掌握感(Mastery) — xUnit 的精神是簡潔。Martin Fowler 曾說:「軟體工程史上,從未有如此少的程式碼對如此多的人產生如此大的影響。」有些實作已經變得過於複雜,自己寫一遍能讓你對工具有完全的掌控。
  • 探索語言(Exploration) — 當作者面對一種新的程式語言時,他會實作 xUnit。寫完前八到十個測試時,他已經探索了日常開發會用到的大部分語言設施。

技巧: 如果你正在學習一門新語言,嘗試用 TDD 的方式實作 xUnit。這是一個絕佳的練習,能同時熟悉語言特性和 TDD 方法論。

Failure vs. Error 的區分#

當你開始使用 xUnit,會發現 assertion failure其他類型的錯誤之間有很大的差異:assertion failure 通常需要更長的除錯時間

因此,大多數 xUnit 實作會區分:

  • Failure — assertion 失敗,代表預期與實際結果不符
  • Error — 測試執行過程中的其他異常(如 NullPointerException)

GUI 介面通常會分開呈現,錯誤(error)往往排在前面顯示。

Test 介面與擴展性#

JUnit 宣告了一個簡單的 Test 介面,TestCaseTestSuite 都實作它:

public interface Test {
    public abstract int countTestCases();
    public abstract void run(TestResult result);
}

如果你希望 JUnit 工具能執行你的測試,你只需實作這個介面即可。

補充: 使用動態型別(optimistic typing)的語言甚至不需要宣告介面——只要實作對應的操作就好。例如,你可以寫一個測試腳本語言,讓 Script 實作 countTestCases() 回傳 1、run() 在失敗時通知 TestResult,就能和一般的 TestCase 一起執行。

本章小結#

Part 2 的核心訊息:

  • Part 2 展示的測試案例序列(test cases)比實作細節更重要——如果你能支援類似的測試案例,就能寫出隔離且可組合的測試
  • 自行實作 xUnit 能帶來掌控感和對語言的深入探索
  • 實務上要注意 failure 與 error 的區分,這會影響除錯效率
  • xUnit 的設計核心是一個簡單的 Test 介面,使得框架具有高度擴展性