厚下巴
厚下巴

過好每一天

Coding Life|TDD (Test-Driven Development) 測試驅動開發

認識TDD這個名詞已久,但對於如何實踐種有一種說不出的陌生感。最近讀了一本DDD的書後,對TDD有新的體悟,想以本篇紀錄與分享。

認識TDD這個名詞已久,但對於如何實踐種有一種說不出的陌生感。最近讀了一本DDD的書後,對TDD有新的體悟,想以本篇紀錄與分享。

本篇會簡單討論TDD觀念以及好處,接著以unit test為實作引導讀者,讓讀者更了解TDD精神,並以一個實作來說明如何實踐。

當然還有更高階層的e2e、整合測試,未來有機會再與各位讀者分享。


TDD:Test-driven development測試驅動開發。是一種開發流程,觀念是「先寫測試,在進入開發工作」。在進行開發工作以前,編寫測試,預先模擬欲測試的情境

好處有:

  1. 可以確保每次改動的狀況,不會改A壞B
  2. 新進人員可以透過測試更加了解每個function在做什麼事,包含呼叫情境,傳入的參數及預期的結果值
  3. 做refactor不再擔心受怕
  4. 在開發前,可以利用測試case, 清楚確認使用情境,減少溝通成本

實際上的開發步驟:

  1. 寫測試。編寫測試,加入test case (此時test會fail)
  2. 寫程式。開始寫code, 目的是要讓 test pass
  3. 重構程式碼。並循環以上步驟refactor 你的code, 但test 還是要pass
ref: https://www.thinktocode.com/2018/02/05/what-is-tdd/

我自己實作起來的痛點有:

  1. 通常寫完function在寫測試會不知道如何下手,然後就不寫
  2. 要測試的功能與其他功能耦合程度太高,不知如何下手
  3. mock一大堆東西,寫測試變得很痛苦

雖然已經有很多文章討論過寫測試的好處,但依我自己的想法,寫test的好處有:

  1. 更清楚將來的function必須符合哪些功用(預期哪些行為)
  2. 該抓出哪些錯誤,code中的error該怎麼噴
  3. 比較敢改code, 改完跑測試就知道有沒有改爆了XD
  4. 可以依功能來測試,不用等別人的部分(如果是多人合作專案的話更為重要)
  5. 可以減少修bug的時間(也不用怕別人改到你的code在拉你去debug的可怕情境)

若看完以上,對於如何實踐還有疑問,接下來會有個實作範例。

以一個演算法的經典題目:704.Binary Search 實作TDD吧!


第一步、確定需求,寫出Test Case.

在Binary Search 這個函數中,讓使用者輸入一個array、一個目標數字,函數會判斷目標數字是否在array中,回傳目標數字的index,或是目標數字沒有在array內則回傳-1。

因此可以先定義一個binary_search函式,會有兩個input,一個是nums(格式是list),另一個是target(格式為int)。此外,這個函式的回傳值是index/-1(格式是int)。

接下來要開始寫測試,根據這的函式的功能,對它的預期行為有:

1.目標數字在數列中,可以正確返回index
2.邊界條件:不論數列長為奇數或偶數,皆可正確返回最首端與最末端的index
3.目標數字不在數列中,可以正確返回-1
4.邊界條件:空數列及長數列皆可正常運行

因此可以寫出test case:

unit test之可以這麼直觀的寫的原因,是因為單元測試有冪等性,每次有相同的input就必須有相同的output,因此可以直接使用斷言(assert)來檢查test case是否被滿足。

這樣就完成測試了!此時跑測試應該會失敗:

值得注意的是,這邊的舉例是將所有test case寫在同一個測試函式內,因此只要有個test case fail了,下面的test case都會跑不到。所以這邊的寫法可以依據需求拆成很多個測試函式,也可以用一個測試類來囊括這個函式的子測試,可以在測試失敗的時候,一目了然fail的測試有哪些。

另外還有一個點,測試通常會獨立放在專案目錄的tests 資料夾下,不會散落在專案之間。




第二步、修改function, 直到測試通過

測試寫完之後的下一步,就是把函式完成。

因為本篇著重於TDD如何實踐,binary search的功能實踐就以較容易上手的二分法來處理。

此時再跑一次測試,就會通過了


第三步、refactor function, 要讓測試一直保持通過狀態

函式的基本功能完成了,接下來就是要讓函式優化。

基本的優化函式可以從三個層面下手:

1. 程式碼的可讀性
2. 記憶體的優化
3. 運算效能的優化

更高層級的架構面的優化將不在這篇討論,有機會再與讀者分享。

如此就完成了一次TDD的開發流程!

完成了一次測試的基本實作,相信大家還是有很多的問題:

1. 要搭配api的呼叫該怎麼寫測試?
2. 在測試中如何操作資料庫?
...

有一個基本的大原則,就是把一個功能再拆分,拆分成一個可以測試的功能跟不能測試的功能,所謂的humble object pattern。例如:把api內的邏輯拆出來成另一個function,對function做unit test。

此外,TDD搭配持續整合(CI)一同使用,可以提升團隊開發的效率。


希望看完這篇,可以讓讀者對於TDD的實作可以有更深入的了解 :)

有想法跟問題也歡迎提出來一同討論!


Reference:

  1. https://tw.alphacamp.co/blog/tdd-test-driven-development-example
  2. http://sd.jtimothyking.com/2006/07/11/twelve-benefits-of-writing-unit-tests-first/
  3. https://tw.alphacamp.co/blog/2015-03-02-tdd-kata
  4. https://www.thinktocode.com/2018/02/05/what-is-tdd/
CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…

发布评论