(譯)TDD(測試驅動開發)的5個步驟

2022-11-13 21:04:53
  原文:5 steps of test-driven development 
  https://developer.ibm.com/articles/5-steps-of-test-driven-development/

作者 Grant Steinfeld
發表於 2020年2月6日

在這篇文章中,我將向你介紹TDD的基礎概念,如果你是敏捷開發實踐者,那麼TDD將是你開發生涯中的最佳實踐。學習TDD是什麼,理解TDD的基本流程並且知道怎麼用單元測試實現TDD。你將會理解為什麼在開發過程中需要使用TDD。

什麼是TDD

TDD顛覆了傳統的開發和測試。TDD不是先寫業務程式碼,而是先寫測試程式碼,寫業務程式碼來反向滿足測試程式碼的校驗。TDD規定你先寫測試程式碼,然後實現程式碼直到剛剛寫的測試程式碼通過。

在TDD中,你先寫測試,觀察其失敗,然後實現程式碼直到測試通過,聽起來很倒退是吧?但是,當你使用這種測試方法時,你生成的程式碼會更整潔,從長遠來看更不容易出錯。

一個單元測試要是簡單的,只涵蓋一小部分邏輯的測試,例如演演算法。單元測試應該是確定性的。這裡的「確定性」的意思是單元測試不應該有副作用,比如呼叫提供隨機或變化資料的外部 API。同理,你將使用模擬資料代替可能隨時間變化的資料。

TDD的五個步驟

TDD流程中有以下五個步驟:

  • 閱讀、理解和處理功能或錯誤請求。
  • 通過編寫單元測試來實現需求。如果你設定了熱過載,因為尚未實現任何程式碼,所以此時單元測試是失敗的。
  • 編寫並實現滿足要求的程式碼。執行所有測試,它們應該通過,如果沒有通過則重複此步驟。
  • 通過重構來整理你的程式碼。
  • 重複。

下圖展示了這些步驟及其敏捷、迴圈和迭代特徵:

該工作流程有時也被稱為紅-綠-重構 (Red-Green-Refactoring),這個稱呼來自週期內測試的狀態。

  • 紅色階段表示程式碼不起作用。
  • 綠色階段表示程式碼都能正常工作,但不是以最佳的方式進行。
  • 藍色階段表示開發人員正在重構程式碼,但他們確信程式碼已被測試覆蓋,這使開發人員有信心修改和改程序式碼。

測試驅動開發和持續整合/持續交付

(CI) 是一種開發實踐,需要開發人員每天多次將程式碼整合到共用儲存庫中。然後通過自動構建驗證每次嵌入,從而使團隊能夠及早發現問題。通過定期整合,你可以快速發現錯誤,並更輕鬆地定位它們。

TDD 產生的單元測試也是持續整合/持續交付 (CI/CD) 過程中不可或缺的一部分。
TDD 的單元測試和持續整合/持續交付管道,如 CircleCI、GoCD 或 Travis CI,它們在提交時執行所有單元測試。

測試在部署管道中執行。如果所有測試都通過,就會進行整合和部署。但是,如果任何測試失敗,該過程就會停止,從而確保構建不會被破壞。

首先設定你的工具、工具鏈和IDE

為了進行測試驅動的開發,你需要先設定你的工具、工具鏈和 IDE。在我們的專案 [code pattern] 中,我們正在開發一個 Node.js 範例,這裡是我們設定的關鍵工具:

  • 用於 Node.js 和 NPM 的 nvm(Node版本管理器):NVM 允許你執行所需的 Node.js 版本並對其進行更改,而不會影響系統node。
  • 用於開發的 npm 庫:

如何編寫失敗的單元測試

以下是幾種不同的方法來編寫失敗的單元測試。

  1. 編寫一個測試,參照程式碼中尚不存在的函數,這將導致測試失敗並出現未找到的錯誤(例如,404 錯誤)。

  2. 更改斷言(assert)語句以使其失敗。斷言語句表示被測試的程式碼預期返回什麼值;這種語句是單元測試的關鍵。斷言語句應反映功能或錯誤請求的響應。

因此,要使單元測試失敗,你需要編寫一個斷言語句,該斷言會在你想要豐富的資料結構中返回一個暫時沒有的值。例如,你的 JSON 返回一個人的姓名,但你的新需求需要包含該人的手機號碼。你將首先編寫斷言語句來包含該人的手機號碼,這將導致它失敗。然後,你將新增業務程式碼來增加該人的電話號碼。

或者,在現實的編碼中:你的斷言語句可能是:assert actualResult == {'track':'foo fighters'}。一旦生產程式碼(函數)被建立,編譯錯誤被解決,404 就會消失,但實際 actualResult 返回的結果可能是像 {} 這樣的空物件。接著再將函數中的返回結果寫死為 {‘track’:‘foo fighters’}。

測試現在將通過(綠色!)。程式碼現在顯然只是臨時的,但你可以得到基本的理解。測試已正確連線到生產程式碼中的某個點。從那裡你可以實現實際的業務邏輯,例如,讀取檔案/db/呼叫外部 API

決定何時編寫單元測試

一般來說,編寫單元測試有兩種情況:

案例 A:你為代表簡明故事的請求編寫單元測試。例如,請求可能是計算特定貨幣兌換所支援的國家/地區數量。我做的第一件事是編寫一個單元測試並看到它失敗。然後,我迭代地更改程式碼,直到單元測試通過。

案例 B:生產中發現的一段錯誤程式碼。此錯誤觸發的問題需要實施修復/修補程式。回到貨幣兌換範例,程式碼執行時,使用者期望在許多國家/地區使用 $USD,但該行為是錯誤的,目前只有一個國家/地區返回。
我做的第一件事是編寫一個單元測試並看到它失敗。然後,更正我的實現程式碼,直到測試通過。這不僅修復了程式碼並消除了錯誤,而且還為我提供了一個可以重複使用的單元測試,以確保這段程式碼保持完整。

總結

大多數程式設計師不使用測試驅動開發來編寫程式碼,但他們應該這樣做。測試驅動的開發可以建立更好的容錯性程式碼。希望你從這篇博文中瞭解 TDD 的理念,並將其融入你的軟體開發實踐中。

下一步

請繼續關注有關如何在 Node.js、Java 和 Python 中進行測試驅動開發的新部落格文章。

資源

非常有幫助的書與文章

Using Test-Driven Development for Microservices, Bill Doerrfeld
Test-driven Java development: Invoke TDD principles for end-to-end application developmnet, 2nd Edition by Farcic, Viktor
Unit testing principles, practices, and patterns, Vladimir Khorikov