一、靜態代碼分析
靜態代碼分析是一種在不執行和運行程序的情況下檢查源代碼的方法。通過這種方法,自動化工具會掃描代碼查找潛在的問題,如bug、安全漏洞或與編碼標準的偏離。靜態代碼分析可以在執行或合併到main分支之前檢查源代碼中的問題。
動態代碼分析是在代碼被執行時,識別運行時的問題,例如性能瓶頸、內存泄露和邏輯缺陷。它輸出的內容是程序的實際運行時行為,包括日誌、跟蹤和性能指標。它在實際或模擬環境中與應用程序並行運行或在應用程序內運行,系統性能開銷很高。
靜態代碼分析工具可以集成在CI/CD管道中,可以確保每次提交都會被自動檢查有無編碼標準錯誤。
二、CI/CD管道是什麼?
CI/CD管道可以理解為:把軟件開發、測試、部署整個過程自動化的一條“流水線”。
1、CI(持續集成,Continuous Integration)
- 拉取代碼:從git倉庫獲取最新代碼
- 編譯 & 構建:mvn clean package -DskipTests=false,使用maven打包,若編譯失敗,直接停止流程。
- 單元測試:JUnit測試類會自動跑起來,比如測試某個類的方法是否正確。若有測試失敗,CI直接報紅,開發者收到通知。
- 靜態代碼檢查:用工具掃描代碼風格與潛在bug。若發現變量名不規範、SQL注入風險、可能的空指針調用。
- 通過以上檢查後,才會進入CD階段。
2、CD(持續交付/持續部署,Continuous Delivery / Continuous Deployment)
CI成功後,進入部署階段。
- 構建Docker鏡像:docker build -t myshop/order-service:1.0。把Spring Boot項目打成鏡像,方便後續在任何環境中執行。
- 推送到鏡像倉庫:docker push registry.company/myshop/order-service:1.0
- 部署到測試環境(自動執行):在Kubernetes上運行kubectl apply -f order-service.yaml,部署新版本到測試集羣,QA團隊或自動化測試程序會在測試環境驗證。
- 部署到生產環境:若是持續交付,需要運維或開發手動點一下“確認上線”;若是持續集成,管道會直接把新版本發佈到生產環境。
3、部署後
監控工具(如普羅米修斯)實時監控服務性能,若新版本有嚴重bug,可以一鍵回滾到上一個穩定版本。單元測試需要比其他測試運行得快(相比集成測試、系統測試等更上層的測試),因為單元測試僅驗證函數/方法等最小單元的局部邏輯,通過Mock/Stub技術隔離數據庫、網絡等外部依賴,可以在內存中直接執行,避免資源初始化、IO等耗時操作;且需再開發階段高頻運行。
三、單元測試
1、概念
單元測試(Unit Test)是指對軟件中最小的可測試單元進行驗證的測試。
在面向對象的程序設計中,單元通常指一個方法(無論是基類、抽象類還是子類中的方法)。它的目的在於確認每個單元是否按照預期正確工作。
(1)編寫原則
① 理論原則
-
快:執行速度要快,才能頻繁運行。
-
獨立:各測試之間互不依賴,順序不影響結果。
-
單一職責:一個測試用例只驗證一個場景。
-
可讀性:命名清晰,讓人一看就懂。
-
自動化:全程自動執行(不需要用户參與輸入,便於持續集成(CI/CD)),結果直接判定為 Pass 或 Fail。
② 規約原則:實際編寫代碼過程中,不同團隊保持自己的規約即可。
-
文件命名:測試文件必須以
_test.go結尾,例如user_service_test.go。 -
方法命名:測試函數必須以
TestXxx開頭,例如TestLogin。 -
方法簽名:函數參數必須是
t *testing.T。 -
文件位置:測試文件和被測文件應放在同一個包中,方便調用與隔離。
③ 衡量原則:寫單元測試是開發者的責任,理論上覆蓋率越高越好,但要結合效率進行權衡:
-
核心優先:先覆蓋核心組件和關鍵業務邏輯。
-
熱點優先:通用的工具類(util)要寫測試,因為使用頻率高,出錯影響大。
-
代表優先:邏輯相似的模塊,只需挑一種典型邏輯寫全套測試。
-
獨立性:每個測試文件獨立,一個測試文件對應一個測試用户;不同用例之間不應互相依賴。
2、設計方法
(1)規範導出法
按照輸入規範,把輸入分成“合法”和“不合法”兩類,每類挑代表值測試。
例:函數要求年齡 18–60 之間,選 30(合法,應該通過),10、70(不合法,應該不通過)。
(2)等價劃分法
用“代表值”覆蓋不同輸入類型,避免重複。
例:年齡合法性檢查
-
有效等價類:18 ≤ age ≤ 60 → 選 30 測試,返回 true
-
無效等價類1:age < 18 → 選 10,返回 false
-
無效等價類2:age > 60 → 選 70,返回 false
(3)邊界值分析法
錯誤常出現在邊界點,重點測試邊界。
例:測試年齡合法性,選 17、18、60、61。
int max(int a, int b) { if (a > b) { return a; } else { return b; } } // 這裏需要測兩條路徑:a > b 和 a <= b
(4)基本路徑測試
白盒測試方法,確保每個獨立邏輯路徑至少執行一次。
經驗公式:圈複雜度 CC = if 條件數 + 1。比如有 2 個 if 條件,就至少需要 3 條測試路徑。
3、demo實戰
在VSCode中新建文件和文件夾,如下圖所示。

在calc.go文件中編寫我們的功能函數(這裏寫了兩個簡單的功能函數,Abs和Add)。

然後在calc文件夾下新建一個以_test為後綴的文件,這裏面會編寫兩個以Test為前綴的測試方法,如下所示。在測試方法內直接調用功能函數,若實際輸出與預期輸出不一致,則報錯。

這時打開終端命令行,在clac文件夾路徑下執行go test命令,就會默認執行該包下的所有以_test結尾的文件。這裏會執行calc_test.go文件。輸入go test -v可以輸出更詳細的信息。

若只測試文件中的指定測試方法,則使用go test -v -run=xxx(方法名)命令。

查看功能代碼對於代碼的覆蓋情況,go test -cover。如果要查看哪25%的代碼沒有被覆蓋到,就要使用go test -cover 。(注意,這個命令要以管理員身份運行,可以在文件中打開,然後在cmd中運行該命令)



我們發現通過這個out文件沒法看出覆蓋率的情況,這時候就需要根據這個文件生成HTML格式的覆蓋率文件,運行下列命令後,會自動跳轉到一個html頁面中。

這時可以在calc.go文件中的TestAbs方法中新增一個測試用例來覆蓋。再次執行go test -cover後,發現此時的測試覆蓋率是100%。


如果在一個測試函數中寫過多的零散的測試用例不太合適,這時候要用到測試組,即將多個測試用例彙集到一起,然後使用循環的方式來一個個測試。


若跑測試用例時報錯,這時沒法很方便看出是哪個測試用例出現了問題,這時候可以用子測試方法,可以清晰看到每一個測試用例的情況。(即給每個用例命名)


這樣就可以清楚看到是哪個自測試FAIL了。
未完待續....