首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 理論與實踐:平衡測試策略(三)—— 以切面驗證設計約束

     編輯:Java 相關知識

前言:AOP 作為低風險測試切入點

面向切面程式設計 (AOP) 是極具潛力的新技術,但採用新技術有風險。最佳策略是沿著可管控風險的路徑採用。若將 AOP 用於策略執行與測試,切面不進入生產環境,不會危及程式碼穩定性,卻能協助開發出高品質軟體,同時也是學習 AOP 的絕佳途徑。

組合測試方法:QA 投資組合理論

QA 目標非找出所有 Bug(不可能),而是提升對程式碼按預期運作的信心。單一測試方法遞減報酬,不同方法發現不同類型錯誤,將 QA 資源分散於測試、程式碼審查、靜態分析,報酬優於集中單一方法。

靜態分析工具 (如 FindBugs) 雖不精確(有誤報、漏報),但能發現真實 Bug,誤報率可接受時極具價值。AOP 驗證設計規則與靜態分析異曲同工:

  • 皆非針對特定方法設計測試案例,而是建立規則檢測器掃描全域違規
  • 皆不需完美無缺即具價值
  • 某些 Bug 模式靜態分析易發現,某些 AOP 更擅長,兩者互補

簡單測試切面:禁止呼叫 System.gc()

動態切面(執行期攔截)

public aspect GcAspect {
    pointcut gcCalls() : call(void java.lang.System.gc());
    
    before() : gcCalls() {
        throw new AssertionError("Don't call System.gc!");
    }
}

需實際執行到該呼叫點才能偵測,靈活性較高,可在切入點執行任意測試程式碼。

靜態切面(編譯期檢查)

public aspect StaticGcAspect {
    pointcut gcCalls() : call(void java.lang.System.gc());
    declare error : gcCalls() : "Don't call System.gc!";
}

編譯期即可偵測,不需執行程式碼。

驗證 Swing 單執行緒規則違規

Swing 元件與模型僅應從事件派發執行緒 (EDT) 存取。此規則幾乎無法靜態強制,違規時程式表面可能正常,潛在嚴重執行緒安全問題。

利用 SwingUtilities.isEventDispatchThread(),在每個 Swing 方法呼叫前插入檢查碼,非 EDT 呼叫即拋出 AssertionError,測試階段即可捕捉違規。

public aspect SwingThreadAspect {
    pointcut swingMethods() : call(* javax.swing..*.*(..)) || call(javax.swing..*.new(..));
    pointcut extendsSwing() : call(* javax.swing.JComponent+.*(..)) 
        || call(* javax.swing..*Model+.*(..)) 
        || call(* javax.swing.text.Document+.*(..));
    pointcut safeMethods() : call(void JComponent.revalidate())
        || call(void JComponent.repaint(..))
        || call(void SwingUtilities.invokeLater(Runnable))
        || call(void JTextPane.replaceSelection(..))
        // ... 其他已知執行緒安全方法
    ;
    pointcut edtMethods() : (swingMethods() || extendsSwing()) && !safeMethods();
    
    before() : edtMethods() {
        if (!SwingUtilities.isEventDispatchThread())
            throw new AssertionError(thisJoinPointStaticPart.getSignature() 
                + " called from " + Thread.currentThread().getName());
    }
}

此切面不完美(安全方法列舉不全、extendsSwing 可能遺漏),但僅用於測試環境,無需生產級完美,即能以低成本發現難以手工測試的執行緒違規。

在除錯與正式實作間切換

常見需求:類別的除錯版本(含詳細日誌、額外檢查)不適合生產,但手動切換建構子呼叫繁瑣易錯。AOP 提供無修改程式碼的切換機制。

範例:以 DebuggingLock 取代 ReentrantLock 偵測死結

public aspect ReentrantLockAspect {
    pointcut newLock() : call(ReentrantLock.new());
    pointcut newLockFair(boolean fair) : call(ReentrantLock.new(boolean)) && args(fair);
    
    ReentrantLock around() : newLock() { return new DebuggingLock(); }
    ReentrantLock around(boolean fair) : newLockFair(fair) { return new DebuggingLock(fair); }
}

DebuggingLock 每次請求鎖時執行死結檢測(Java SE 6 可透過 ThreadMXBean),效能較差但能更快發現死結。另一版本 OrderHistoryLock 記錄鎖獲取順序歷史,偵測不一致鎖順序(最常見死結成因),空間開銷較大但涵蓋面更廣。

結語

本文示範的屬於「策略執行切面」。無論是應用程式設計策略(如「此方法僅允許從類別 X 呼叫」)或 API 契約(如 Swing 單執行緒規則、EJB 限制),皆可在開發測試階段以切面驗證合規性。無論生產環境是否採用 AOP,它都是測試工具箱中的利器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved