前情提要
目前參加的讀書會有點進入了倦怠期,
參加的人數出席不穩定,會議過程有點感覺像是在照本宣科。
所以我們安排每隔幾周,由不同分享一下個人工作上不同的經驗。
讓負責分享章節的人可以有更多時間準備,
同時也可以讓聽者換換口味同時喘口氣。
對於我來說,可以重新整理一下過往的經驗。
試著能不能更有組織的分享單元測試這個工具與知識。
同時,因為是線上分享會的形式,當我無法觀察觀眾的表情時,
遠端會議應該怎麼進行才能更有效呢?
進行方式
- 先問為什麼?
- 輪流發表一下想法(TimeBox:60s)
Live Coding(如果是現場分享,我會希望多一點實作)
思考問題
- (單元)測試是什麼呢 ?
- 為什麼要寫(單元)測試 ?
- 什麼樣的情境需要(單元)測試?
這裡的設計是 What、Why、When,我覺得改成 What、When→How、Why 會更好。
主要的目的是引導聽眾思考 Why,這裡我的受眾應該為開發人員,
實務上還有可能會是 QA(測試人員)、產品經理(PM/PO*)、維運人員、
營運部門甚至是老闆或客戶。
舉例情境如下:
問:單元測試或是測試是什麼呢?
答:是一種自動化的對程式的方法進行驗証 blab blab ….
捕捉到關鍵字 自動化
方法
…可以延伸提問
- 測試不一定要
自動化
,但是單元測試建議要作到一鍵測試(半自動) - 單元不一定是
方法
,但我們可以以方法
當作最小粒度來討論,
後續的分享會對粒度再作討論,但重要的是團隊的認知要一致
進一步提問**什麼樣的情境需要(單元)測試?**,
如果現場乾掉了,可以改成以下的提問:
- 上 Prod 前要不要測試?
- 那上 QA/Stage 要不要測試?
- 開發新功能要不要測試 ?
- 那 HotFix 要不要測試 ?
- 拿到一份全新沒看過的 Source Code 要不要測試 ?
你會發現測試無所不在,那我們為什麼(Why)要測試啊?
我的回答,(單元)測試是品質保証的一種手段,
如果上線前,(單元)測試全過,
我就對被測試保護的方法(情境)有信心不會壞。
同樣的,在部署到其它環境時測試通過,我對品質信也就會跟著提昇。
換句話說,測試是品質的可量化指標。
可能的問題:測試全過,上線還是有可能會壞掉啊
回答: 壞掉是什麼樣的情境 ? 是不是一種沒有被測試保護到的情境 ?
是的話只要加上情境即可,一般來說我們應該可以作到 96 % 以上的常態情境
剩下考慮發生機率與重要性,應該加上測試就加測試保護,
這裡不限定單元測試也包含整合/端到端/手動等其它測試。
另一個情境是開發新功能,
任何功能都是方法與流程的組合,
當你在開發新方法或新功能時,如果能同時寫好測試*,
那你就會有一份具備可量化品質的代碼,
同時還可以帶來的額外好處是,當有一個新人拿到你的代碼時,
他會有一份測試可以當作規格書來閱讀。
注意:這裡可以代入很多其它相關的概念
比如說:TDD、Code Review、可讀性、Pair Programming
理解方法與流程是最耗時的一件事,
如果有測試可以節省相當多的時間,
但要注意測試也是人寫的,如果為了寫而寫,很容易寫出垃圾測試,
試著在開發流程上引入 Pair Programming、Code Review、Pull Request 等…機制
另外當代碼成長到一定程度時,
如果需要重構,測試將提供一定的保護網(看覆蓋率多少)。
但要小心實務上不應該過度追求覆蓋率。
這裡提供一個觀念,不要為了 Design Pattern 而 Design Pattern
但是即有的代碼需要重構之時,Design Pattern 可以提供像是燈塔般的指引作用
只要巧妙的設計測試案例,走向目標。
此外,當代碼有了單元測試的保護,
在開發日常的除錯作業將會有很大的幫助,
測試可以幫你快速的定位錯誤。
即使測試並未攔截到錯誤的話,那也表示你發現了一個前所未有的情境,
而只要加上這個測試情境,再修改代碼,未來這個情境將不會再有錯誤。
經典書籍對單元測試的定義
1 | 一個單元測試是一段自動化的程式碼,這段程式會呼叫被測試的工作單元, |
F.I.R.S.T Principles
1 | - Fast : 快速→不夠快就不會被頻繁執行 |
小結
上面提到很多工程面的實踐,
也不僅限於單元測試,更多的是測試與品質的關係。
一再強調的觀念是:
測試是品質的可量化指標。
測試是品質的可量化指標。
測試是品質的可量化指標。
而回到工程面上來說,作為第一線的品質把關,
開發人員本來就應該對自已的代碼提供單元測試,
而 TDD、Test As Document、Refactoring、Design Pattern 等…是環環相扣的工程實踐,
Pair Programming、Code Review、Pull Request 等…是工程流程的實踐
單元測試正是那個將各種實踐結合在一起的一種工具。
在心法上,要將單元測試視作工具,而非聖盃,TDD
或是覆蓋率
也是同樣的道理,御物而不御於物。
1 | 可用的軟體 重於 詳盡的文件 |
引用一下敏捷宣言:
可用的軟體 重於 100%覆蓋率的代碼
可用的軟體 重於 測試全過的代碼
右側項目雖然有其價值,但我們更重視左側項目。
而這裡可用的最低標準,我認為是符合品質期待。
First Test
第一個單元測試,加法計算器
Case:Add(+) : 1 + 1 = 2
1 | [ ] |
Production 代碼
1 | public class Calculator |
Next Case
用 Test Case 逼出邏輯,用最簡單的方法實踐
1 | [ ] |
1 | public class Calculator |
3A 原則
Arrange(準備、初始化)
如果 Arrange 過長會是一個壞味道,
表示這方法相依太多參數、服務或模組
範例:target = new Calculator();
- first number is
2
- second number is
1
Act(執行/呼叫受測行為)
target.Add(2, 1)
Assert(驗証)
- `Assert.Equal(3, acted result);
不要過度追求可讀性,而將測試程式變得難以理解,
可以使用測試驗証框架(ex:Fluent Assertions),
或是抽出方法來增加可讀性,但比起可讀,更重要是可理解。
彩蛋
後續的 Live Coding 會讓參加者完成後續的方法測試,
加法、減法、乘法、除法…
特別計算到除法的除零邏輯時,應該拋出錯誤。
趁這個機會可以介紹如何驗証 Exception。
進一步可以介紹如何使用
fluentassertions
。
1 | [ ] |
(fin)