選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
8 分鐘閱讀時間 (1543 個字)

為什麼程式需要單元測試? - 概念篇

Photo by Hello I'm Nik on Unsplash Photo by Hello I'm Nik on Unsplash

此篇文章主要帶大家了解為什麼程式需要單元測試,有興趣就往下看吧!

工程師接到任務後,大概會有幾個問題在大腦徘徊:

  1. 不管三七二十一,先寫再說吧!
  2. 新需求會修改到原有程式,只好硬著頭皮先寫,之後再說吧!
  3. 時代在變,做法也在變,看別人之前寫的程式碼,手癢想改一下,先改再說吧!
  4. 開發過程中,看到一些沒在使用的程式碼能不能刪阿?沒關係,先刪再說吧! 

 ❗ 如果你有上述任一行為,代表你正在讓系統可維護性逐漸降低,而且會越來越嚴重!

你可能會說:

  1. 礙於時程關係,程式能跑就好,有差嗎?
  2. 請你改程式,不是要你改壞程式,好嗎?
  3. 別人寫好的程式碼就是好的,不要亂改啦!
  4. 程式碼別亂刪,不然到時候不能動吼...

我的見解

  1. 時程內完成可以跑的程式我覺得沒什麼大問題,但還是會建議要寫單元測試出來!
  2. 工程師完成需求,盡可能要維持開放封閉原則(OCP)情況下來修改程式,這跟寫不寫單元測試並無衝突,但如果你有寫的話,兩者就會相輔相成。

  3. 有了單元測試,就可以不被綁手綁腳,專注於找出更好的做法並刪除一些不需要的程式碼。

上面講這麼多就是為了將各位帶入單元測試的好究竟在哪裡?

單元測試

介紹

⭐ 其實單元測試你可以把它想像成走路線小遊戲...

當今天有一個要求(Request)打了某一個 Web API,根據需求所撰寫的程式,是否有辦法走完每一條路線(使用案例)呢?

下圖所示為程式碼涵蓋率 100% 的情況(代表每一路線均有走完):

why unit test 1

而當今天因為新需求,而有了新路線時,如下圖:

why unit test 2

 ❗ D 路線因需求變化而產生,因為 D 修改的程式對於 A、B、C 來說是不穩定的因素,需要特別留意…

寫完單元測試後才漸漸明白它可以幫我們快速做回歸測試(Regression Testing),D 測試寫出來,直接一併測 A、B、C、D,就能知道 D 的修改是否會影響 A、B、C 哦!

好處

  • 改 A 壞 B 馬上現形
  • 縮短開發者回歸測試時間
  • 程式回傳變得可預期
  • 程式愈符合物件導向設計原則
  • 容易快速重構程式碼
  • 排除過度設計的程式碼
  • 降低程式過度耦合

難處

  • 對於一個未曾有過撰寫單元測試經驗的開發者來說,撰寫單元測試所耗用的時間可能會大於實際撰寫程式碼的時間,因為思維會變成「撰寫可測試程式的測試程式」,所以多數人會選擇放棄撰寫單元測試…
  • 需先判斷是否有寫單元測試的價值才去寫,因為對於需求不確定、價值性不高去寫反而會浪費時間,因為需求變動,勢必單元測試程式也會變動,不太可能一成不變!

迷思解惑(歡迎補充與指教)

(一)單元測試 != 整合測試

時常看到有人會把單元測試理解為整合測試,但兩者並不一樣,別再搞混了!!!!

舉幾個例子,如果有人問你以下問題:

  1. 寫單元測試要不要和 Database 一起測試阿?
  2. 寫單元測試要不要和 File System IO 一起測試阿?

大概就可以知道他對單元測試的了解程度,我來看的話,就是直接出局!!

單元測試關注的是測試程式本身邏輯,所以必須要把外部依賴(Database、File System IO)全部排除掉才有辦法往下進行!

(二)Private 方法到底要不要測試?

一般來說不會主動去測試 Private 方法,因為在測試 Public 方法時,Private 方法很有可能會是 Public 方法的一部分,所以其實就會一併測試到了。

我們終究是關注對外開放的行為(Public),所以才說不會特別主動測試 Private 方法,你可以想像本來 Private 方法的內容就是寫在 Public 方法當中,只是你把它抽出來而已,所以要視為一個整體來寫單元測試

(三)測試涵蓋率要拉到接近 100% 嗎?

其實我覺得能拉到 80 ~ 90 % 就行了,只要確保含有大部分重要邏輯的程式有被涵蓋到就可以

(四)寫出來的測試程式有可能都不變動嗎?

不變動的大前提是測試案例沒有任何變化,才有可能!

只要測試案例有變化,測試程式也要跟著做相對應調整,所以不大可能不變化!

舉個例子:

比如我 1 月寫出來的單元測試程式,直到 7 月都仍然可以使用的話,這就代表你的測試案例其實沒有顯著變化,因為需求沒有太大變動。

但程式總會因為新需求而有新變化,這時可能就要審視當下寫的單元測試是否還能應付當下情況,若不行就表示要調整了!

總結

  1. 寫單元測試的效益,起初可能不明顯,但它可以很大程度降低後續維護成本。
  2. 搭配 CI 可以做到自動化測試並分析程式涵蓋率。
  3. 若發現程式寫不了單元測試,很有可能表示程式沒有盡可能符合物件導向設計原則(SRP、OCP、LSP、ISP、DIP)導致。
  4. 有單元測試保護,程式愛怎麼寫就怎麼寫,反正只要符合預期就好。

寫單元測試這件事要養成習慣不容易,可能也有很多人認為是沒必要的,但老實說單元測試能帶來的效益還是很驚人的!

之前在網路上有看到網友的一則留言,分享給各位:

好的程式架構和 OO 概念,都是透過被迫寫 UT 建立起來的。

如果能看懂這句話,表示你對單元測試有一定的了解,我當初看得是痛哭流涕那種...

感謝各位把文看完,若有一些疑問,歡迎指教與補充!

這篇主要講概念,下篇就會進入案例實作。

QuEyeCIA 測試之路
Ansible #8 - Ad-Hoc 指令[1]

相關文章

 

評論 3

George Chou (周孚陽) 於 2021/05/23, 週日 07:05

以下幾點請教:
1. 所以隔離會用到的 Mock 套件有什麼推薦的嗎? 還是你都手動建立 Test Double XD
2. 如果今天需求異動,應該是多加符合需求的 Unit Test,還是直接去改舊有 Unit Test 的內容?
3. 測式涵蓋度是 SonarQube 算的嗎?還是 .NET 有什麼特殊的套件可以直接算出?
4. 我曾經看過一個神秘的三角形,最底層就是 Unit Test,跟您說的程式都是 UT 堆起來的不知是否有關聯?

以下幾點請教: 1. 所以隔離會用到的 Mock 套件有什麼推薦的嗎? 還是你都手動建立 Test Double XD 2. 如果今天需求異動,應該是多加符合需求的 Unit Test,還是直接去改舊有 Unit Test 的內容? 3. 測式涵蓋度是 SonarQube 算的嗎?還是 .NET 有什麼特殊的套件可以直接算出? 4. 我曾經看過一個神秘的三角形,最底層就是 Unit Test,跟您說的程式都是 UT 堆起來的不知是否有關聯?
Neil Tsai (蔡慶霖) 於 2021/05/24, 週一 09:55

1. C# 的話,我是比較常在用 Moq,我還是盡量會找套件來幫忙,能不手動就不手動 XD
2. 這我可能會看異動的邏輯本身牽涉到什麼程度,如果是新案例,確實可能會傾向寫新測試,舊有測試內容就不會動,但如果牽涉的邏輯很深,就不太可能說完全不動舊有的測試(ex. 原程式直接把呼叫外部相依的做法整個換掉,這個應該就很難維持說舊有測試不變動)。
3. 程式涵蓋率不是 SonarQube 直接去算出來的(可參考 Test Coverage & Execution),SonarQube 主要是分析測試報告中的內容並將那些數據視覺化,所以就有辦法在 SonarQube 看到程式碼涵蓋程度,另外 .NET 應該有蠻多測試套件可以測試完就直接產生出測試報告(如果未來有找到不錯的,會在分享讓各位知道)。
4. 我是覺得寫不寫 UT 比較看個人,要想寫出讓人信服的程式碼,UT 可能就會是必經之路,路途中肯定是撞牆撞到不知道在幹嘛,但細細品味後就能海闊天空。至於程式是不是 UT 堆出來的?可能就可以參考 TDD(測試驅動開發流程),當我們以測試為前提來開發時,或許真的就是用 UT 來堆出程式...

1. C# 的話,我是比較常在用 [url=https://github.com/moq/moq4]Moq[/url],我還是盡量會找套件來幫忙,能不手動就不手動 XD 2. 這我可能會看異動的邏輯本身牽涉到什麼程度,如果是新案例,確實可能會傾向寫新測試,舊有測試內容就不會動,但如果牽涉的邏輯很深,就不太可能說完全不動舊有的測試(ex. 原程式直接把呼叫外部相依的做法整個換掉,這個應該就很難維持說舊有測試不變動)。 3. 程式涵蓋率不是 SonarQube 直接去算出來的(可參考 [url=https://docs.sonarqube.org/latest/analysis/coverage/]Test Coverage & Execution[/url]),SonarQube 主要是分析測試報告中的內容並將那些數據視覺化,所以就有辦法在 SonarQube 看到程式碼涵蓋程度,另外 .NET 應該有蠻多測試套件可以測試完就直接產生出測試報告(如果未來有找到不錯的,會在分享讓各位知道)。 4. 我是覺得寫不寫 UT 比較看個人,要想寫出讓人信服的程式碼,UT 可能就會是必經之路,路途中肯定是撞牆撞到不知道在幹嘛,但細細品味後就能海闊天空。至於程式是不是 UT 堆出來的?可能就可以參考 TDD(測試驅動開發流程),當我們以測試為前提來開發時,或許真的就是用 UT 來堆出程式...
George Chou (周孚陽) 於 2021/05/24, 週一 11:41

1. Java 的話就是 Mockito 了,手動會起肖
2. 同感
3. 抱歉,我描述錯誤XD,Java 的 maven 有跟測試相關的套件整合(JaCoCo)可以產 Test Coverage 的結果,再餵給 SonarQube
4. TDD(遠目),加油

感謝交流 ^_^

1. Java 的話就是 Mockito 了,手動會起肖 2. 同感 3. 抱歉,我描述錯誤XD,Java 的 maven 有跟測試相關的套件整合(JaCoCo)可以產 Test Coverage 的結果,再餵給 SonarQube 4. TDD(遠目),加油 感謝交流 ^_^
已經注冊了? 這裡登入
Guest
2024/05/04, 週六

Captcha 圖像