Soft_Job 板


LINE

因為大家的討論都很基於心法 實作上相對很模糊 利用這個機會也跟大家請教實作上的方式 因為最近工作被指派要針對公司產品的程式做整理,其實運作都還好 只是大家功能是一層疊一層,一堆巢狀邏輯,跟依賴中的依賴 也沒有任何的測試跟註解,當然也沒有任何測試 舉個例子 有個功能是儲值,可以接受銀行、信用卡、或是一個臨時帳號 然後儲值前需要身分驗證,然後銀行的話要檢查是不是有綁定 總之每個方式都有一些共用,或是非共用的行為 目前的程式像這樣 func 儲值(方式){ switch 方式{ case 方式1: if 符合條件1 { if 符合條件2 { if 呼叫api-1 成功{ 更新介面1 } } } case 方式2: 要符合不同的巢狀條件,然後呼叫另一隻api,一樣根據結果更新不同的介面 case 方式3: 又是不同的條件跟api } } 像這樣的程式,不知道測試怎麼寫? 單看這個函式的cyclomatic complexity,大概是20幾吧 如果把測試寫成涵蓋所有的可能 像是=> 當選擇方式1儲值,且不符合條件1,就要如何如何 整個測試就寫不完了 而且條件本身又依賴於其他的api或是系統參數 就算寫完了測試,涵蓋率雖然高,但我覺得是沒有解決核心的問題 我就只是要確認我程式本身的邏輯沒問題,不需要涵蓋別人的東西 這邊我實在很好奇大家的作法是怎樣? 是跳過不做,還是真的就寫一個涵蓋所有可能的測試 我的選擇是先重構啦 先定義一個抽象的RechargeCommand介面 包含執行儲值操作所需的方法 其中execute()會統一回傳一個Result物件,代表儲值結果 接下來為每種儲值方式實作具體的命令 例如BankRechargeCommand、CreditCardRechargeCommand和AutoRechargeCommand等 然後一些通用的行為,像是登入驗證、密碼輸入等,用裝飾者模式 定義一個RechargeCommandDecorator抽象類別,繼承自RechargeCommand介面 最後為每種需要共用的行為創建一個具體的裝飾者,繼承自RechargeCommandDecorator 例如 LoginCheckDecorator和BioAuthDecorator 呼叫的時候,根據需要去組合不同的儲值方式,跟需要的共同行為 回傳值也都是相同的Result物件 呼叫會像這樣(我是ios swift) let bankRechargeCommand: RechargeCommand = BankRechargeCommand() let loginCheckDecorator = LoginCheckDecorator(bankRechargeCommand) let bioAuthDecorator = BioAuthDecorator(loginCheckDecorator) let result = bioAuthDecorator.execute() 呼叫api的部分用策略模式做依賴注入 在command實作中,接收一個RechargeAPIService,預設是真實呼叫 做單元測試的時候就單獨傳入mock service 測試也改成針對不同的儲值方式做測試 而不是去涵蓋所有的可能路徑 這樣就可以大幅減少測試案例的撰寫,也可以節省複製貼上的時間 我只要確定我的內部邏輯正確,理論上不管怎樣的路徑應該都不會出錯 也不會被綁定在外部依賴 整個流程大概就是把一個巢狀判斷函式 更改為 command+decorator 模式,根據需要呼叫與包裝對應的行為 然後外部的依賴透過注入,在測試時使用mock避免被干擾 也請大家看一下 不知道這樣的修改是不是可以 還是應該以涵蓋所有的可能狀況為優先 @@? ※ 引述《TonyQ (得理饒人)》之銘言: : 先說我不是故意要回兩篇, : 但剛看到 landlord (就 joey chen, 江湖名 91) 在 FB 的回應,我覺得也蠻好的, : 他說他最近在忙沒空過來,我問過他之後幫他轉過來。 : 以下基本上逐字照轉 : (source from https://tinyurl.com/rxyerfyw ) : 其實講到底根本原因反而是跟產品程式碼的設計能力有關, : 產品程式設計得越好,測試程式越容易寫,越好測。 : 真正需要在測試中做假模擬(隔離)的部分, : 屬於外部(擁有權不在我們手上的部分), : 例如外部系統的服務(走通訊協定出去,且不屬於我們可以維護跟上版的服務)、 : 三方(package/SDK)。而 DB, redis之類的 cache 甚至是不需要特別被隔離開的。 : 這是由於現代科技的便利,讓我們有機會把越來越接近端到端測試的一類, : 比例逐步拉高的可行性比過去容易得多。 : 另一個重點則是當設計越來越偏向高內聚,simple design, : 把 code smell 消除到最後回很自然地提煉出 domain model, : 有了 domain model, : 最複雜的 domain logic 處理一堆散落資料的邏輯都被內聚到model裡面, : 沒有 application 層的依賴,model 的單元測試也很好寫。 : 結論: : 1. 要有能力在 legacy 上重構出可測試性 : 2. 要有能力做出穩定的端到端測試 : 3. 要能精煉設計,將散落的資料內聚在一起 : 來代表 domain 的概念提供 domain 的行為, : 因為設計上本來就沒有外層的依賴,model的方法也都精簡短小,甚至鮮少回傳值, : 自然 API 易用性跟測試都可以比過去萬惡的三層式架構+內嵌無限層依賴注入 : 的手風琴架構來得簡單跟好測許多。 : 現在大部分的依賴(注入)都不是本質上需要的,而是被開發人員硬生生切得支離破碎的。 : 補充一下 TonyQ 內文最後一點: : 「如果都沒被報 bug,你也沒有修改它的需要時,幫它加測試幹嘛?」 : 這超級重要的,這種情況下加測試往往適得其反, : 只會建立偽陽性/陰性的測試結果,勞民傷財還造成干擾。 : ※ 引述《TonyQ (得理饒人)》之銘言: : : 底下這是比較「野性」」的作法,算是實務專案的經驗: : : 其實我覺得你到一個完全沒有測試的專案,要分兩個策略: : : 1. 補重要主線的 integration test 反正哪邊常被報修就補哪邊。 : : 如果一開始補不上去就先做下一點,理論上常被報修的地方會一直出現在下一點, : : 累積多了就可以變成1了。 : : 2. 假設自己是維護歷史功能,提昇自己維護部分的可測試性。 : : 舉實際案例好了,假設我今天再做一個算金流手續費的專案, : : 發現過去算手續費假設有11個地方寫了11次好了,所謂的高耦合不外乎如此。 : : 我會先寫個 util 把輸入跟輸出「去狀態化」,然後針對這個 util 寫, : : 然後這個 util 的單位以「去狀態化」成本可控,可在手邊開發時間允許的範圍進行。 : : 白話文:我橫豎都得手動測試,那就把手動測試的部分, : : 盡可能的透過 test code 來進行。 : : 如果不想接著維護的話也很單純,任務結束後把 test code comment 掉或移除就行。 : : 題外話,11個地方,我會選擇先取代一個地方, : : 然後等其他10個地方有需求變更時,一個個整併,補強測試條件。 : : 很多人會說,那為什麼不一次改11個,理由是你的開發時間跟成本允不允許。 : : 更重要的是你的QA資源夠不夠,因為寫正常的Test最累的是準備測試情境, : : 這真的是會花掉比寫test更多的時間。 : : 如果列不出完整場景,貿然修改既有的code只是在裸奔。 : : 有需求的部分是被迫裸奔,但沒需求的部分不用主動當暴露狂, : : 等待驗證過再慢慢統一。 : : 大原則就是:你橫豎都是要測試的,只是手測還是寫程式測,除了跟 gui 有關的東西, : : 多數的情況下寫程式測試都還是比較省時間的。 : : 更棒的地方是,在這種策略下,你往往可以用比同事少30% 的時間完成任務, : : 而且因為你的測試成本比較低,所以品質也會比較好,出問題的時候也更容易對焦。 : : 然後我通常是會跟同事說我寫了幾個 test case, : : 他們願意看就看,不願意看我就放著。不會勉強他們加入。 : : 如果你做不到可以用比不寫測試更短的時間來完成任務, : : 那你學的測試根本性的就有問題,不寫也罷。XD : : 3. 極端情況: 如果都沒被報bug,需求也都很小? : : 那你操心他幹嘛 XD --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 59.120.34.91 (臺灣)
※ 文章網址: https://webptt.com/m.aspx?n=bbs/Soft_Job/M.1714634857.A.75E.html
1F:→ landlord: 剛好之前寫過類似的題目,有帶到重構的過程跟影片 05/02 15:35
2F:→ landlord: https://bit.ly/4b0B3Nb 05/02 15:36
3F:→ landlord: 當時也有朋友支援各語言版本庫,給大家參考一下 05/02 15:36
4F:→ landlord: 當然,寫測試的話還是得知道有哪些情境,才能用測試描述 05/02 15:37
5F:→ landlord: 但這個重構的過程,即使沒測試,有pair跟 code review 05/02 15:37
6F:→ landlord: 也不會有太大的成本跟風險問題 05/02 15:37
神...Orz
7F:→ brucetu: 你的最後一段假設就是在說如果單元測試都沒問題整合測試 05/02 15:39
8F:→ brucetu: 跟上線理論上都不會爆掉 05/02 15:39
9F:→ brucetu: 實際上呢?如果這個理論正確那就不用寫整合測試了 05/02 15:40
10F:→ brucetu: 所以要保證不爆掉當然需要所有的輸入變數的可能性跟路徑 05/02 15:41
11F:→ brucetu: 都測試過,符合預期,才有可能保證上線不爆掉 05/02 15:41
其實因為我的是app,所以其實我這段測試其實更偏向整合測試而不是單元測試 只是也很好奇 所謂的整合測試,在牽涉到外部的時候,需要一起測嗎? 因為我的測試程式本身就包含了登入檢查、生物辨識、呼叫api 只是這些部份我使用的是mock 這些物件的測試應該用其他的方式或包裝,不應該影響儲值這件事跟儲值的測試 加上我回傳的是一個通用的Result物件 這個Result要不是成功(包含成功儲值的相關資訊),要不是失敗(失敗的相關資訊) 流程中如果任何一個環節失敗了,對呼叫端來說都是收到失敗的Result 這時候就是處理失敗的介面與邏輯 原則上我還是會在測試中補上測失敗的測試案例 但應該就是幾個常見的,不會去涵蓋每一個 我的目標還是很簡單,我要確認的是程式這邊的邏輯有沒有問題 輸入變數已經被command模式限制了,以這個案例來說,好像不知道還有啥變數 且不管哪個環節錯,他就是拋Result<Error> 額外的測試,說真的,我覺得自己按一下比較快 理論上也不應該出現沒捕捉到的錯誤 真的有...大概也是很稀少的案例,這個就交給內部測試或是實際使用者吧 冏rz ※ 編輯: langrisser19 (59.120.34.91 臺灣), 05/02/2024 15:59:22
12F:→ TonyQ: "如果任何一個環節失敗了,對呼叫端來說都是收到失敗的" 05/02 16:00
13F:→ TonyQ: 這個可能 app 比較沒感覺,但是鑑別不同的失敗對debug重要 05/02 16:01
14F:→ TonyQ: 題外話你這個情境算是很特定的情境了,我自己在這個時候會 05/02 16:35
15F:→ TonyQ: 思考的問題是,「假設我的程式出錯了,我能不能避開最大的 05/02 16:35
16F:→ TonyQ: 傷害。」 這也是一個可以考慮的事情。 05/02 16:36
17F:→ brucetu: 理論上牽涉到第三方服務的時候你要mock第三方服務 05/02 19:48
18F:→ brucetu: 實務上第三方有提供測試區的話我會直接開一個測試區db直 05/02 19:49
19F:→ brucetu: 接在上面測試 05/02 19:49
20F:推 wulouise: 寫一個新的加測試 05/02 21:30
21F:推 OldTjikko: 巢狀太多了,重構看看能不能guard clauses 05/03 00:21
22F:→ yamagishi: 太巢惹, ealry return 用一下該測的東西分一下你自然知 05/03 08:32
23F:→ yamagishi: 道 test 要怎麼寫 05/03 08:32
24F:→ new122851: 當你發現UT寫不下去時,你要的是重構 05/03 19:56
25F:→ rogerlarger: 把每個if內的code都分別做成function內,然後再寫un 05/10 14:08
26F:→ rogerlarger: ittest? 05/10 14:08







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:BuyTogether站內搜尋

TOP