學習開發時,一定會遇到一道牆。
「單元測試、整合測試、UI 測試……到底有什麼不同?」
先說結論。
三種測試的驗證範圍不同。單元測試檢查一個函式,整合測試檢查模組之間的連接,UI 測試則確認完整的使用者畫面流程。
告訴我們該如何混合這三種測試的指引,就是「測試金字塔」。今天會結合我的經驗,簡單說明這個概念。
先看重點摘要
為了方便忙碌的讀者,先整理如下。
- 單元測試:只驗證一個函式或類別。快速且大量撰寫。
- 整合測試:驗證多個模組連接後是否正常運作。數量適中。
- UI 測試:像點擊實際畫面一樣驗證完整流程。慢且少量。
- 測試金字塔:底部的單元測試要寬、頂部的 UI 測試要窄。
記住這四行,就理解一半了。接著逐一拆解。
單元測試、整合測試、UI 測試有什麼不同?
這是最容易混淆的部分,先從比喻開始。
想像一下你正在製造汽車。
單元測試就像檢查一顆螺絲或螺栓是否符合規格,是非常小的單位。
整合測試則是檢查引擎和變速箱連接後,是否能順利咬合運轉。
UI 測試就像坐進完成的車裡、發動引擎並實際駕駛。
用表格比較,可以整理如下。
| 分類 | 驗證範圍 | 速度 | 撰寫數量 |
|---|---|---|---|
| 單元測試 | 一個函式或類別 | 非常快(ms) | 多 |
| 整合測試 | 模組間整合 | 普通(以秒計) | 中等 |
| UI 測試 | 完整使用者流程 | 慢(數十秒) | 少 |
核心差異在於「要看多大的範圍」。
範圍越廣,越接近實際使用環境,但也會越慢,管理更加棘手。
單元測試就是這種感覺
光說不容易有概念,來看一個簡短範例。
下面是只鎖定驗證兩個數字相加函式的單元測試。
// 只驗證加法函式的單元測試
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
@Test func 加法_2_相加_3是_5() {
#expect(add(2, 3) == 5)
}
不需要外部資料庫,也不需要畫面,只要單獨取出一個函式確認即可。
因此執行會在眨眼間完成。即使執行數百個,也只需要幾秒。
我最常使用單元測試,就是喜歡這種快速感。修改程式碼後可以立即確認。
什麼是測試金字塔?
現在進入今天的主角:測試金字塔。
這是 Mike Cohn 在 2009 年的著作《Succeeding with Agile》中提出的概念,主張將測試堆成三角形。
- 最底層(寬):單元測試 — 最多
- 中間:整合測試 — 適量
- 最上層(窄):UI 測試 — 最少
為什麼是這種形狀?
因為越往下越快速、便宜。即使執行數千個單元測試,也不會造成負擔。
相反地,UI 測試速度慢,畫面稍微變動就很容易失敗,維護成本也很高。
大量使用便宜又快速的測試,少量使用昂貴又緩慢的測試。這就是測試金字塔的全部。
如果把金字塔倒過來堆,就稱為「冰淇淋甜筒」反模式。這代表 UI 測試很多,卻沒有單元測試,很容易被緩慢又脆弱的測試拖住。
那比例該怎麼設定?
這是很多人都想知道的問題。
常被引用的標準大約是單元 70%:整合 20%:UI 10%。
不過這不是絕對法則,應根據專案性質彈性調整。
例如後端 API 伺服器,提高整合測試的比例會更實際。Google 等組織也強調維持「金字塔形狀」的方向,而不是追求精確數字。
再分享一個我親身經歷得到的建議。
不要執著於數字。重要的是養成檢查「是否過度依賴緩慢且容易失敗的測試」的習慣。
常見問題(Q&A)
Q. E2E 測試和 UI 測試是一樣的嗎?
兩者幾乎會互換使用,但嚴格來說不同。E2E(End-to-End)指從使用者角度驗證完整流程,UI 測試則著重於其中的畫面操作。實務上常常混用。
Q. 只要把整合測試寫好,就可以不做單元測試嗎?
不建議。整合測試很難精確指出問題發生在哪裡。有單元測試才能快速縮小原因範圍。
Q. 測試必須一開始就全部寫好嗎?
不用。我建議先從核心邏輯的單元測試開始。很多人試圖一開始就建立完美的金字塔,最後反而精疲力竭。
這三者的差異只看文字可能很抽象,但親自撰寫程式碼後就會有切身體會。
用今天學到的內容,先為一個小函式加上單元測試吧。累積這種感覺後,其他部分自然會跟上。加油!

