常常聽到說寫測試可以確保程式碼品質、幫助我們不會修壞程式碼,
那具體來說測試到底是什麼樣的東西呢?

單元測試 (Unit Testing)

單元測試是一組驗證功能的程式碼,這段程式會呼叫被測試的工作單元,並且針對結果進行驗證,
為了可以在開發過程中或是發布之前可以找到邏輯錯誤的功能,通常都會加在 CI/CD 的流程中。

一組單元測試通常有三個步驟:

  1. Arrange:準備 => 初始化並設定好要測驗的物件
  2. Act:操作 => 執行要驗證的方法
  3. Assert:驗證 => 對結果進行驗證是不是符合預期
1
2
3
4
5
6
7
8
9
10
11
func testNumberToolIsPositive() throws {

// Arrange:準備
let numberTool = NumberTool()

// Act:操作
let result = numberTool.isPositive(input: 999)

// Assert:驗證
XCTAssert(result)
}

單元測試的關鍵特質

  1. 自動化且可被重複執行的
  2. 容易實作的
  3. 容易執行的 (隨便按個鈕就可以)
  4. 非臨時性的 (這個測試到第二天第三天仍有意義,並可以執行)
  5. 執行速度很快的
  6. 執行結果一致的
  7. 完全獨立的 (不依賴其他因素或測試)
  8. 測試目標清楚的 (發生錯誤可以明確知道哪裡有問題)

假設你的測試有不符合上面單元測試的特質,很有可能你的是屬於”整合測試”

整合測試 (Integration Testing)

當你的測試依賴某種環境或是帶有不定因素(例如系統時間、網路狀態),
或是用到了真實的檔案系統或是資料庫等等,這些測試都將屬於整合測試。

1
2
3
4
5
6
7
8
9
10
11
12
13
func testDataManagerGetDataFromServer() throws {

let dataManager = DataManager()
dataManager.getDataFromServer { // ---> 假設此方法呼叫了Api
result in
switch result{
case .success(let data):
XCTAssert(true)
case .failure(let error):
XCTAssert(false)
}
}
}

整合測試依然有必要,但有個問題就是一次測試太多東西,
無法確定到底是誰壞了,到底是前端 API 物件寫壞還是 Server 目前是壞掉的呢?


寫好單元測試就好了嗎?


測試金字塔 (Testing Pyramid)

前端往往包含大量 UI 的互動和多樣的資料來源,
如果只依靠單元測試,可能沒辦法完全測試出可能的問題,所以衍伸出不同層級的測試方式,也就是測試金字塔。
網路上有各種不同的版本,依照不同產品可衍生不同分層,但是想表達的概念都大同小異!

這裡簡單說明最常見的三層:

定義 特點
UI測試(UI Testing) 從使用者角度的測試,實際操作App 速度慢、撰寫成本高、維護成本高
整合測試(Integration Testing) 測試包含實際的伺服器、DB 介於中間
單元測試(Unit Testing) 單純測試邏輯,並隔離所有外部因素 速度快、撰寫成本低、維護成本低

撰寫測試的順序是由下至上,像是蓋房子一樣,
通常單元測試是涵蓋範圍最大的,需要針對大部分的 基底物件方法和商業邏輯 進行測試,
而整合測試、UI測試應該會越來越少才對,對 關鍵路徑(Critical Path) 進行測試即可。

推薦書單

單元測試的藝術(The Art of Unit Testing)

最重要的三個章節:

  • 第1章:單元測試定義
  • 第8章:好的單元測試
  • 第11章:如何讓程式變可測試