※ 引述《shane87123 (陽光大肥宅)》之銘言:
: 先說我對 Unit test 的看法:測試單元(可能是 function)的邏輯是否正確
: 好,進入正題
: 小弟最近剛工作,稍微讀了一下負責的 project 的程式碼後,
: 要開始開發 Unit test。
: 現況是,各個 file (.c) dependency 很重,
: 常常會有一份 code 內其實呼叫了很多別份 code 的 function,
: 舉例來說
: A() {
: B();
: C();
: if (check)
: D();
: }
: 族繁不及備載,
: 而我目前設計有兩個方向,
: 1.
: 將 B() C() D() 全部 fake ,單純去測試 A() 的邏輯是否正確
: 這樣做感覺上會比較單純,一個 test case 只去 test A(),
: 而且不需要去 include B() C() D() 的 header,
: 這樣一來 build 起來也比較容易,因為 include 那些 header 又會 dependency 到其他檔
: 情況會非常複雜
: 缺點是 coverage 比較差,B() C() D()要額外去寫 test case
先說結論,先都不要寫。
Legacy system 要先補大範圍的 integration test,確定整體的行為是對的。
如果 code 沒有要再改,不用補細部 unit tests。
原因是因為,原本 API 可能因為設計不良,導致無法寫 unit test
得先 refactor 才有辦法讓它變成 testable,這情況就要先 refactor 再補 UT
而 integration test 會一定程度減少 refactor 造成 regression bugs。
再來正名運動一下,fake/mock/stub 都是 test double,但用途不一樣
https://martinfowler.com/articles/mocksArentStubs.html
fake 仍然是一個大致完整的實作,只是比較簡化。
例如本來是 on-disk 的 key-value database,fake 就可以用 in-memory 的一個
dictionary 物件換掉,簡化許多但是 API 有提供 "一樣的行為 (key 的讀寫)"
stub 則是多半只會 return 固定的 constant,本身沒實作行為
用 test double 主要好處是減少 dependency,然後測試速度會快。
測試大規模系統,如果讀寫檔案,讀寫資料庫都是真的去讀,那就牽扯到跨系統整合
會花超多時間跑,也比較是 integration test
Unit test 的目標要能快速給予回饋,所以很強調執行速度。用 test double 換掉
同時能提升 isolation,也避免一些意外狀況造成測試失敗。
例如程式邏輯明明沒錯,卻因為外部因素而失敗跑不過,
所以 unit test 的書上多推廣用 test double。
但實務上,持續維護 test double 和真實物件的行為一致,是一件高成本而且困難的事
如果你改變了真實物件的行為,所有有用 test double 的地方都要重新改寫
而且 fake 物件實作有是可能會錯,那反而會製造更多問題
所以 To fake or not to fake? That is the question.
後來有些書上就提倡 prefer real object,能用真的就盡量不要用假的。
根據專案的狀況,你可以選擇最適合的方式,沒有絕對正確答案
然後目標不是高 coverage。目標是最 business critical 的 path 都有測試到。
coverage 要高很簡單,全部 function 呼叫一次,然後不要檢查任何東西
coverage 就會 100%,但什麼也沒測試到。
只要關鍵行為都測試到了,不用太糾結 coverage 數字高不高
: 2.
: 直接把他們 include 進來,build failed 就 include,直到 build 過為止
: 這樣的好處是不用去實作 B() C() D() 的 fake,
: 但就會讓整個 unit test 的 dependency 很重
這有時候是合理的作法。
Unit test 是依照"行為"拆分,不是依照 class/function 拆分。
不是一個 function 就對應一個 test,而是以"user 可見的行為" 來拆。
一個功能/行為常常需要多個 functions 組合,這些 private function 都是"實作細節"
是沒有必要測試的,你只需要測試 public interface.
同理,一個功能由 A/B/C 三部分組成,A 呼叫 B 和 C,但實際上對外使用者只用 A
那 B/C 可以視為 A 的實作細節,這時候可以只針對 A 做 test
一個 class 的 public API 可以是另外一個 class 的 implementation detail
所以不是一個 class 就要一個 test,實作細節不用測,要測試大的行為。
: 個人偏向1.,畢竟 unit test 就是去測試 function 的邏輯性,
: 在其他 function 對測試 function 沒有 side effect 的情況下(如不會改變某變數的值?
: 將他們 fake 掉而只是單純的去 test 該 function 而已
: 但我第一次接觸,不太知道何時應該去 fake (或 mock) 一個 function QQ
: 我只是有這兩種想法,兩個其實天差地遠XDD
: