測試驅動開發(TDD)2020總結——原理篇

 

 

我是一名喜歡追求高質量代碼和高效率工做的軟件開發工程師,所以我學習 SOLID 和 Simple Design 等原則、閱讀優秀的開源代碼、閱讀相關的書籍、學習軟件過程方法和真實項目實踐,可是在追求高質量代碼的道路上,總感受目前的知識還不能幫我塑形成一種思惟框架。在 2018 年年初機緣巧合閱讀了TDD(測試驅動開發)培訓錄這篇文章,瞬間欣喜若狂!程序員

到如今接觸 TDD 將近一年,期間由於沉不下心只閱讀了不多的資料就在項目中實踐了一段時間,獲得的效果還不錯,自覺得已經很理解 TDD 實踐和背後的思想,結果在不斷閱讀相關書籍和關於一些 TDD 的討論中不斷暴露本身的無知,發現本身就像站在「達克效應」曲線的愚昧之巔,原來大部分自認爲正確的知識都是不許確甚至是錯誤的。不過我很享受這種過程,在學習的過程當中不斷驗證本身的知識是很是有趣的,這使我變得更有自知之明的同時也在不斷突破自身的認知上限。編程

接下來我會經過圖文的方式總結這段時間來對 TDD 的實踐和思考,以便於沉澱自身對 TDD 的理解,但願對讀者有所幫助,也但願讀者能夠指點一二,集思廣益才能離真相更進一步。框架

 

範圍.


 

TDD (Test Driven Development) 在不一樣的圈子、不一樣的角色的認知中可能會有不一樣的理解,有人可能會理解成 ATDD(Acceptance Test Driven Development),也有人可能會理解成 UTDD(Unit Test Driven Development),爲了不產生歧義, 文章涉及到 TDD 專指 UTDD(Unit Test Driven Development),即 「單元測試驅動開發」
 

什麼是 TDD


 


之前很片面的認爲 TDD = XP 的測試優先原則 + 重構,認爲 TDD 只是經過單元測試來推進代碼的編寫,而後經過重構來優化程序的內部結構。這很容易被理解成只須要先寫單元測試就能夠驅動出高質量的代碼,直到我精讀 Kent Beck 的著做《測試驅動開發》和不斷實踐思考以後才總算窺探到 TDD 藏在冰山下的面貌:
Kent Beck:「測試驅動開發不是一種測試技術。它是一種分析技術、設計技術,更是一種組織全部開發活動的技術」。

分析技術: 體如今對問題域的分析,當問題尚未被分解成一個個可操做的任務時,分析技術就派上用場,例如需求分析、任務拆分和任務規劃等,《實例化需求》這本書能夠給予必定的幫助做用。函數

設計技術: 測試驅動代碼的設計和功能的實現,而後驅動代碼的再設計和重構,在持續細微的反饋中改善代碼。單元測試

組織全部開發活動的技術: TDD 很好地組織了測試、開發和重構活動,但又不只限於此,好比實施 TDD 的前置活動包括需求分析、任務拆分和規劃活動,這使得 TDD 具備很是好的擴展性。學習


TDD 的目標


Kent Beck 在他的著做《Test-Driven Development》一書中提到:「代碼簡潔可用這句言簡意賅的話,正是 TDD 所追求的目標」。測試

對於如何保證「代碼簡潔可用」可使用分而治之的方法,先達到「可用」目標,再追求「簡潔」目標。優化

可用: 保證代碼經過自動化測試。編碼

代碼簡潔: 在不一樣階段人們對簡潔的理解程度也不同,不過遵循的原則差很少,例如 OOD 的 SOLID 原則,Kent Beck 的 Simple Design 原則等。設計

雖然有不少因素妨礙咱們獲得整潔的代碼,甚至可用的代碼,無需徵求太多意見,只須要採用 TDD 的開發方式來驅動出簡潔可用的代碼。



TDD 的規則

在 TDD 的過程當中,須要遵循兩條簡單的規則:

  1. 僅在自動測試失敗時才編寫新代碼
  2. 消除重複設計(去除沒必要要的依賴關係),優化設計結構(逐漸使代碼通常化)

第一條規則的言下之意是每次只編寫剛恰好使測試經過的代碼,而且只在測試運行失敗的時候才編寫新的代碼,由於每次增長的代碼少,即便有問題定位起來也很是快,確保咱們能夠遵循小步快跑的節奏;第二條規則就是讓小步快跑更加踏實,在自動化測試的支撐下,經過重構環節消除代碼的壞味道來避免代碼日漸腐爛,爲接下來編碼打造一個溫馨的環境

關注點分離是這兩條規則隱含的另外一個很是重要的原則。其表達的含義指在編碼階段先達到代碼「可用」的目標,在重構階段再追求「簡潔」目標,每次只關注一件事!!!

TDD 的口號

 

 

簡單來講,不可運行/可運行/重構——這正是測試驅動開發的口號,也是 TDD 的核心。在這個閉環中,每個階段的輸出都會成爲下一階段的輸入。

  1. 不可運行——寫一個功能最小完備的單元測試,並使得該單元測試編譯失敗。
  2. 可運行——快速編寫剛恰好使測試經過的代碼,不須要考慮太多,甚至可使用一些不合理的方法。
  3. 重構——消除剛剛編碼過程引入的重複設計,優化設計結構。

假設這樣的開發方式是可能的,那我採用 TDD 真正的動機是什麼?

採用 TDD 的動機

  • 控制編程過程當中的憂慮感。

有一個有趣的想象,當我感受壓力越大,自身就越不想去作足夠多的測試。當知道本身作的測試不夠時,就會增長自身的壓力,由於我擔憂本身寫的代碼有 BUG,對本身編寫的代碼不夠自信,這是一種心態上的變化。此時測試是開發人員的試金石,能夠將對壓力的恐懼變爲平日的雜事,採用自動化測試,就有機會選擇恐懼的程度。

  • 把控編程過程當中的反饋與決策之間的差距。

若是我作了一週的規劃,而且量化成一個個可操做的任務寫到 to-do list,而後使用測試驅動編碼,把完成的任務像這樣劃掉,那麼個人工做目標將變得很是清晰,由於我明確工期,明確待辦事項,明確難點,能夠在持續細微的反饋中有意識地作一些適當的調整,好比添加新的任務,刪除冗餘的測試;還有一點更加讓人振奮,我能夠知道我大概何時能夠完工。項目經理對軟件開發進度能夠更精確的把握。


TDD 的總體流程

 

 

  • 想一下我要作什麼,想一想如何測試它,而後寫一個小測試。思考所需的類、接口、輸入和輸出。
  • 編寫足夠的代碼使測試失敗(明確失敗總比模模糊糊的感受要好)。
  • 編寫剛恰好使測試經過的代碼(保證以前編寫的測試也須要經過)。
  • 運行並觀察全部測試。若是沒有經過,則如今解決它,錯誤只會落在新加入的代碼中。
  • 若是有任何重複的邏輯或沒法解釋的代碼,重構能夠消除重複並提升表達能力(減小耦合,增長內聚力)。
  • 再次運行測試驗證重構是否引入新的錯誤。若是沒有經過,極可能是在重構時犯了一些錯誤,須要當即修復並從新運行,直到全部測試經過。
  • 重複上述步驟,直到找不到更多驅動編寫新代碼的測試。

使測試程序可運行的三條策略:


 

  1. 僞實現——能夠返回一個常量或變量,而後調整僞實現,直至僞實現變成可接受的實現代碼。
  2. 明顯實現——直接將實現代碼鍵入,由於已經明確如何編寫實現代碼。
  3. 三角法——當我明確輸入和輸出但殊不知道它背後的設計和實現是什麼時,可使用三角法,原理是先用簡單的可運行的例子做爲參考的信息源,而後推出測試的明顯實現。詳細信息在參考資源中給出。

這三條規則的目的是達到代碼的「可用」目標,只須要鍵入咱們認爲正確的代碼使測試程序儘快經過便可。


TDD 的難點

  • 缺少軟件質量意識
  • 缺少必定程度的程序設計能力,很難設計出高內聚低耦合、意圖清晰的結構和代碼。
  • 缺少分析需求並進行任務分解和規劃的能力,很容易在還沒開始 TDD 的時候就被打亂了節奏。
  • 缺少合適的測試環境和測試規範。
  • 測試優先的習慣難以養成。
  • 重構手法不熟練。

TDD 疑問

  • 都說小步快跑,具體步伐是多小?

不管是測試程序覆蓋的範圍仍是重構時的中間步驟,TDD 建議是採用儘可能小的步伐(測試沒法再拆分,微小的重構),可是也沒有強制必定按照這種步伐,不一樣人的步伐能夠不一樣,能夠在實踐中不斷尋找適合本身的步伐,可是前提必須儘可能小。

  • 什麼須要測試?什麼不須要測試?

除了那些不寫測試還能對本身的代碼感到很是自信的人以外,這取決於本身的經驗和對代碼的信心程度。若是某些代碼本身認爲即便不須要測試,運行和重構時也很是有信心,就能夠不須要測試,好比大部分 set get ;相反,若是去掉會讓本身感到不安,就須要考慮加入測試。

  • 爲何須要遵循不可運行/可運行/重構這個順序,不能夠採用其它順序嗎?

這個問題《測試驅動開發》做者 Kent Beck 也很難去證實,由於沒有專門的人真正去作過這個統計,因此他表示不否定可能存在一些更好的順序設計。

  • 爲何每次在可運行階段只編寫「可用」代碼?

由於要儘快使測試運行起來,這樣能夠下降來自系統的反饋週期,若是可以快速持續獲得來自系統的反饋,那麼就能夠持續保持小步快跑的節奏。若是能夠短期實現一個好的設計,寫出優雅簡潔的代碼,那麼在一開始 TDD 的時候,就應該採用最好的設計,由於這樣的效率會比較高。

  • TDD 是銀彈嗎?

TDD 不是銀彈,遇到問題須要尋找核心痛點是什麼,而後再對症下藥。

單元測試

與 ATDD 不一樣, UTDD 主要面向的是開發人員,因此 UTDD 在這裏主要關注的是軟件內部的質量屬性,若是說軟件的外部質量體如今「缺陷數」和「缺陷率」等指標,那軟件的內部質量屬性體如今代碼的「可測試性」、「可讀性」和「可擴展性」等,這些幾乎是每一位軟件開發工程師的追求。「單元測試」做爲 TDD 的產物之一,爲了把控軟件內部的質量屬性,一般會使用到自動化「單元測試」做爲軟件質量保證的「根基」。

在計算機編程中,單元測試(英語:Unit Testing),一般由軟件開發人員編寫,用於確保他們所寫的代碼匹配軟件需求和遵循開發目標,是針對程序模塊(軟件設計的最小單位)來進行正確性檢驗的測試工做。
每一個理想的測試案例獨立於其它案例;爲測試時隔離模塊,常用 stubs、mock 或 fake 等測試馬甲程序。
一般來講,程序員每修改一次程序就會進行最少一次單元測試,在編寫程序的先後極可能要進行屢次單元測試,以證明程序達到軟件規格書要求的工做目標。

從維基百科的描述中能夠看出單元測試擁有以下特色

  1. 開發人員編寫。
  2. 測試函數/方法(TDD 的粒度更小)。
  3. 用於對代碼進行正確性檢驗。
  4. 編碼先後和修改代碼時都會運行單元測試。
  5. 常用 stubs、mock 或 fake 等測試馬甲程序確保每個單元測試之間互相獨立(正交)

這裏從我我的的角度對單元測試進行簡單的分析,但對「單元測試」的理解,或者是它所處的位置仍是不夠清晰,因此接下來我使用了「測試金字塔」模型來幫助我站在一個更高的視角理解「單元測試」。

測試金字塔

 

 

上圖的「測試金字塔」模型按照運行速度和投入成本兩個維度對不一樣階段的測試工做進行很是直觀的可視化,能夠看到單元測試是位於「測試金字塔」的最底部,很明顯「單元測試」相對於其它不一樣階段的測試工做,擁有速度快(運行效率),成本低(維護成本)的優點,同時也是做爲上層測試工做的支撐,體現了「單元測試」的重要程度。

總結


文章純理論總結了 TDD 的全貌和一些 TDD 實踐過程當中的策略,包括 TDD 的難點和疑問,文章屢次提到「反饋」一詞是由於 TDD 是一種引入大量底層反饋的技術(得益於自動化測試),這些反饋使得很快就能看到行動的結果,使用 TDD,它將會在實踐的過程當中學會如何雕琢咱們的代碼,從而獲得穩定的面向對象設計、可維護和高質量的系統。

後續

紙上得來終覺淺,惟有知行合一,經過理論指導實踐,在實踐中不斷總結經驗,不斷驗證本身的知識,才能不斷對 TDD 有更深刻更正確的理解。接下來將計劃出幾篇文章演示使用 TDD 如何解決一些真實的案例的總結,以便於提升本身的 TDD 技藝。

相關文章
相關標籤/搜索