PTT推薦

Re: [討論] Unit test 的撰寫請益

看板Soft_Job標題Re: [討論] Unit test 的撰寫請益作者
HZYSoft
(PCMan)
時間推噓33 推:33 噓:0 →:8

※ 引述《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

: --
: ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.25.70.74 (臺灣)
: ※ 文章網址: https://www.ptt.cc/Soft_Job/M.1667904696.A.B26
: 推 vi000246: 你可以先讀重構相關的書 11/08 18:53: → GooseLover: 如果模組化有做好,那你1.做得是正確的事情。正常來 11/08 19:12: → GooseLover: 說就是Function跟Process分開測,最後再來個整合測試 11/08 19:12: → GooseLover: 。然後不要抗拒為各別Function寫Unittest 11/08 19:12: 我本來預期就是為個別檔案的個別function寫unit test,不過不確定這樣做是不是有符合u
: nit test的特性
: 推 s06yji3: 單元測試的話1 11/08 19:15: → foreverk: 用1吧,如果要測試的function長文章那樣,那本來就應該 11/08 19:22: → foreverk: 花時間寫BCD的test case 11/08 19:22: 謝謝各位大大,那我可以放心去fake了
: 推 s06yji3: 不知道你用什麼語言,通常會有tool幫你攔截dep的function 11/08 19:28: → s06yji3: 然後去呼叫對應的fake function 11/08 19:28: 應是用google test,沒錯,他可以去呼叫fake function。這部分的實作應該沒什麼問題: 推 MoonCode: 對了 不寫這個測試會怎樣? 11/08 20:14: ※ 編輯: shane87123 (114.25.70.74 臺灣), 11/08/2022 20:15:55
: → ssccg: 合起來測也是種測法,只是那就不是unit test了 11/08 21:14: → angusyu: 專案如果沒有在切介面,直接硬fake會寫到懷疑人生 11/08 21:24: 推 yamakazi: gmock 就是1啊 11/08 22:41: → yamakazi: gmock gtest框架都有了,你想得到的問題大公司都想到了 11/08 22:42: → yamakazi: ,直接整套拿去用就好 11/08 22:42: 推 drajan: 沒切介面就趕快 refactor 沒測試的軟體能跑嗎 11/08 23:01: 推 sniper2824: 1 11/08 23:51: 推 viper9709: 推二樓 11/08 23:57: 推 Lipraxde: 沒出問題的 legacy code 就別想著幫別人加 UT 了,頂多 11/09 00:00: → Lipraxde: 做做整合測試,別把自己搞到懷疑人生 11/09 00:00: 推 lovdkkkk: 不確定目前程式的情況, 假如目前程式很亂, 有可能需要先 11/09 00:17: → lovdkkkk: 做 2 快速加個整合測試, 重構一下, 之後再做 1 11/09 00:17: → leo08210917: 介面切好 弄懂IoC、DI做mock很快 UT也方便 11/09 01:07: → leo08210917: 舊的可以用防腐層切開 弄整合測試就好 11/09 01:08: 推 prag222: 雖我單元測試沒啥經驗,但說真的就是程式太鳥才依賴性 11/09 02:21: → prag222: 太高,相信用物件導向或設計模式都可以切乾淨的 11/09 02:21: → Lipraxde: 只會更糟吧XDDD 11/09 07:22: → bnd0327: 如果BCD沒有被其他函式使用,直接用真的BCD測也無妨 11/09 08:53: 推 wulouise: 2不算UT,但是你在refactor前可能會寫出2那樣的情況 11/09 12:10: → wulouise: 最好先用test framework測整合測試再來refactor 11/09 12:12: 推 andy831020: 絕對是1 最小單元去測試 11/09 15:23: 推 lestibournes: 最近也在工作上嘗試導入,覺得應該是1。但光mock就 11/09 18:28: → lestibournes: 一堆東西,還要擔心測試把程式碼綁死(因為mock/fa 11/09 18:28: → lestibournes: ke的部分已經明確宣告在測試內),還是先硬著頭皮 11/09 18:28: → lestibournes: 先寫了QQ 11/09 18:28: 推 CoNsTaR: 切 dependency 用 mock,有測試環境的問題用 fake 11/09 22:33: → acgotaku: 請善用DI,然後再寫的時候儘量將ABC低耦合 確保你分開測 11/10 01:36: → acgotaku: ABC的時候不需要在mock,做假資料的時候儘量是真實狀況 11/10 01:38: 推 s8952889: 當然是1吧,如果你今天改B的程式碼結果A的單元測試錯了 11/10 12:51: → s8952889: 很奇怪 11/10 12:51: → s8952889: 不過其實在單元測試的檔案寫整合測試也是沒問題的吧 11/10 12:52

--
Sent from PCMan on PCMan's PC

--

※ PTT留言評論
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.225.92.96 (臺灣)
PTT 網址
※ 編輯: HZYSoft (36.225.92.96 臺灣), 11/10/2022 23:26:00

Hsins11/10 23:24Sent from PCMan on PCMan's PC by the author of PCMan -

Hsins11/10 23:24PCMan.

※ 編輯: HZYSoft (36.225.92.96 臺灣), 11/10/2022 23:27:39

Sean042811/10 23:28Good

wulouise11/10 23:32不過實作細節要分到多細還是看個人考量..

wulouise11/10 23:33不然整個chrome只測UI好像也符合這個分法(?)

peyton8711/10 23:34推legacy系統要先大範圍integration test。之前摸索時先

peyton8711/10 23:36搞了些mock物件,弄老半天只測一小塊。直接做必要的fake

peyton8711/10 23:36然後大塊功能的integration test,至少能測。

peyton8711/10 23:38因為先有integration test,才成功做了些重構。

drajan11/11 00:25同意在缺乏好的模組化跟測試的前提下 整合測試是最優先的

drajan11/11 00:25確保最大範圍的抽象行為是正確的再去改內部的程式

CKNTUErnie11/11 00:27

※ 編輯: HZYSoft (36.225.92.96 臺灣), 11/11/2022 00:34:13

noahleft11/11 01:45

yuinami11/11 02:03

whatzup112411/11 02:23

bnd032711/11 08:21推推

lchcoding11/11 08:40拜讀

CRPKT11/11 08:40這篇每一段的概念都值得推一次

sck92111/11 10:57

alan511/11 10:58還在那邊想是誰觀念這麼正確...

ntps60803orz11/11 11:25為什麼全部function呼叫一次就可以達到100% coverag

ntps60803orz11/11 11:25e?不考慮條件式嗎?

rizman2811/11 12:03

ManOfSteel11/11 12:04

Walkers11/11 12:19先推

newhandfun11/11 12:40我們也是先整合再單元,原因就是覺得之後可能會重構

richer660511/11 12:44

lukelan11/11 13:54pcman 大神推推

scottxxx66611/11 14:09

shibin11/11 15:34

sky8042011/11 17:35先推再看

d0788020111/11 19:43

bewitchsky11/11 22:22

jskblack11/11 22:34推受用

devilkool11/12 00:27

yupog200311/12 10:13我們公司完全照著這篇文章的觀念在寫unit test

a478288711/12 16:45這篇寫的很好 感謝分享

jasonwung11/13 01:18

remember6911/13 14:49受用 推

uziel11/15 07:35同意,要測就測抽象行為

zapion11/16 09:47推這篇