JUnit 等框架普及單元測試,但其效率低、僅測試單一程式碼片段、重用性差(元件 A 的測試難套用至元件 B)。典型流程:發現 Bug → 寫測試案例 → 修復 → 回歸測試 → 版控 → 記錄。雖提供信心與紀錄,但屬被動式、逐個實例解決,成本高昂。
NASA 每開發人員配 20 測試人員仍有缺陷,證明「找出所有 Bug」不可能。QA 目標應為在有限資源下最大化對程式碼正確性的信心。測試、程式碼審查、靜態分析各有遞減報酬曲線,組合投資報酬優於單一投入。
靜態分析不執行程式碼即可分析,擅長發現測試與審查遺漏的錯誤。FindBugs 開源工具內建多種 Bug 模式偵測器,支援自訂偵測器打包為外掛。核心偵測器經調優,誤報率 < 50%,較傳統 lint 工具實用得多。
傳統作法:針對具體 Bug 寫測試。進階作法:識別 Bug 模式 → 開發偵測器 → 掃描全專案。
BigDecimal 不可變,add() 回傳新物件。常見錯誤:
if (!qualifiesForFreeShipping())
totalCost.add(getShippingCost()); // 錯誤!回傳值被丟棄
此模式適用於 BigInteger、String、Color 等所有不可變類別的工廠方法。
編寫測試類別,javap -c 反編譯觀察位元碼:
public void doBadStuff();
Code:
0: invokestatic #2; //Method java/lang/System.gc:()V
3: return
靜態方法呼叫對應 invokestatic 指令,操作元為 java/lang/System.gc:()V。
public class CallSystemGC extends BytecodeScanningDetector {
public void sawOpcode(int seen) {
if (seen == INVOKESTATIC) {
if (getClassConstantOperand().equals("java/lang/System")
&& getNameConstantOperand().equals("gc")) {
bugReporter.reportBug(new BugInstance("SYSTEM_GC", NORMAL_PRIORITY)
.addClassAndMethod(this).addSourceLine(this));
}
}
}
}
findbugs.xml:部署描述符,定義偵測器類別、速度、回報的 Bug 模式messages.xml:資源檔,定義多語系顯示字串對 JDK 1.4.2 類庫掃描,發現 com.sun.imageio 套件中的 JPEGImageReader、JPEGImageWriter 呼叫 System.gc()!庫端誤用導致應用程式效能下降,開發者難以察覺。證明靜態分析的彈性:一次開發,處處可用。
靜態分析與自訂偵測器是提升軟體品質極有效手段。為已知 Bug 模式建立偵測器,不僅能掃描當前專案,更能保護當前與未來所有專案免受同類錯誤侵害。額外投入將換來品質上的豐厚回報。