單元測試

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

  • 不管三七二十一,先寫再說吧!

  • 新需求會修改到原有程式,只好硬著頭皮先寫,之後再說吧!

  • 時代在變,做法也在變,看別人之前寫的程式碼,手癢想改一下,先改再說吧!

  • 開發過程中,看到一些沒在使用的程式碼能不能刪阿?沒關係,先刪再說吧! ❗ 如果你有上述任一行為,代表你正在讓系統可維護性逐漸降低,而且會越來越嚴重! 你可能會說:

  • 礙於時程關係,程式能跑就好,有差嗎?

  • 請你改程式,不是要你改壞程式,好嗎?

  • 別人寫好的程式碼就是好的,不要亂改啦!

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

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

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

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

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

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

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

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

好處

  • 改 A 壞 B 馬上現形

  • 縮短開發者回歸測試時間

  • 程式回傳變得可預期

  • 程式愈符合物件導向設計原則

  • 容易快速重構程式碼

  • 排除過度設計的程式碼

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

迷思解惑(歡迎補充與指教) (一)單元測試 != 整合測試 時常看到有人會把單元測試理解為整合測試,但兩者並不一樣,別再搞混了!!!!

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

  • 寫單元測試要不要和 Database 一起測試阿?

  • 寫單元測試要不要和 File System IO 一起測試阿? 大概就可以知道他對單元測試的了解程度,我來看的話,就是直接出局!!

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

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

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

(三)測試涵蓋率要拉到接近 100% 嗎? 其實我覺得能拉到 80 ~ 90 % 就行了,只要確保含有大部分重要邏輯的程式有被涵蓋到就可以。

(四)寫出來的測試程式有可能都不變動嗎? 不變動的大前提是測試案例沒有任何變化,才有可能!

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

舉個例子:

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

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

在軟體開發中,測試是一個非常重要的步驟,它有助於確保軟體能夠正常運作並符合用戶的需求。程式開發的測試通常可以分為以下幾種:

  1. 單元測試(Unit Testing):單元測試是針對程式中的個別單元進行的,通常由開發人員自行進行。這些測試通常涉及到測試單個函數或方法的輸入和輸出,以確保它們能夠正確運作。單元測試可以使用各種工具來自動化測試,例如Junit、NUnit、PHPUnit等。

  2. 整合測試(Integration Testing):整合測試是針對不同單元之間的互動進行的,用來確保各個單元可以正確地協同運作。整合測試通常包括測試不同程式模塊之間的相互作用、測試服務之間的集成和測試數據庫等。整合測試可以通過手動測試或自動化測試來完成,例如使用Selenium、Appium等自動化測試工具。

  3. 系統測試(System Testing):系統測試是針對整個系統進行的,包括程式、資料庫、網絡等。它旨在確保整個系統能夠滿足用戶的需求,並能夠正常運作。系統測試通常包括功能測試、性能測試、壓力測試等。功能測試是驗證系統是否符合需求的主要方式,性能測試和壓力測試則用於驗證系統在不同負載下的運行狀態。

  4. 靜態測試(Static Testing):靜態測試是在不執行程式的情況下進行的,通常是通過程式碼檢查、代碼審查等方式進行的,目的是發現潛在的錯誤。靜態測試可以使用各種工具來自動化,例如靜態代碼分析工具(例如SonarQube、CodeClimate等)或代碼審查工具(例如Crucible、CodeCollaborator等)。

  5. 動態測試(Dynamic Testing):動態測試是在執行程式的情況下進行的,通常涉及到對軟件進行實際的操作和驗證。動態測試可以進行自動化或手動測試,例如功能測試、壓力測試、性能測試等。

  6. 接受測試(Acceptance Testing):接受測試是由客戶或最終用戶進行的,以驗證軟件是否符合他們的需求和期望。接受測試通常涉及到使用用戶案例或測試腳本來模擬實際使用情況,以驗證系統是否可以滿足用戶需求。

總體而言,測試的目的是確保軟件的正確性和穩定性。不同的測試方法可以應用於不同的測試階段和不同的應用場景。開發團隊可以根據需要組合這些測試方法,並使用自動化測試工具來提高效率和精確度。

Last updated