Struts 應用單元測試面臨兩大挑戰:Servlet 容器依賴與協作類依賴。StrutsTestCase 模擬容器,EasyMock 動態模擬協作類,但兩者整合存在「Action 實例在 actionPerform() 後才創建,無法提前注入 Mock」的時序衝突。本文對比 OOP 方案與 AOP 方案,證明 AOP 以切點穿越中間對象,優雅解決注入時序問題。
MockStrutsTestCase 模擬容器

嘗試子類化 ActionServlet、RequestProcessor 暴露 Action 實例,但時序圖揭示核心矛盾:

Mock 注入必須在 actionPerform() 前,但 Action 實例要在 actionPerform() 內部由 RequestProcessor 創建,形成「雞生蛋蛋生雞」死結。
繼承 RequestProcessor 重寫 processActionCreate() 雖能攔截創建,但導致測試類爆炸(每個測試方法需不同 Mock 期望 → 不同子類 → 不同 struts-config.xml),治理成本極高。

SimpleAction:被測 Action,協作 ActionServiceSimpleActionTest:繼承 MockStrutsTestCase,實現 StrutsActionPreExecuteListenerStrutsActionPreExecuteNotifier:切面類,通知測試類注入 Mock
// 1. 捕獲測試方法執行,暴露測試類實例
pointcut mockStrutsTest(StrutsActionPreExecuteListener actionTest): ...
// 2. 捕獲 Action execute() 執行,限制在測試方法調用流內,暴露 Action 與測試類實例
pointcut strutsActionExecute(Action action, StrutsActionPreExecuteListener actionTest): ...
// 3. Before Advice:在 Action execute 前通知測試類,傳遞 Action 實例供注入
before(Action action, StrutsActionPreExecuteListener actionTest): strutsActionExecute(action, actionTest) {
actionTest.onPreExecute(action); // 測試類注入 Mock
}



AOP 方案將 Mock 創建、期望設置、注入局部化在單個測試類內,靈活支持上述所有場景。
同樣會導致「每種 Mock 期望組合需一個切面類」,退化為 OOP 方案的子類爆炸問題。監聽器模式將配置局部化,保持測試類內聚。
AOP 的連接點模型使我們能「穿越」ActionServlet、RequestProcessor 等中間對象,直達問題核心。以極「節儉」的橫切關注點定義,產生直觀簡潔的解決方案。恰當使用 AOP,改進模塊化,產出更清晰可維護的代碼。
本文不僅解決 Struts 單元測試注入難題,更展示 AOP 解決「難以在對象創建前攔截」這類經典問題的威力。