一引言
測試驅動開發在減少開發努力的同時也改進了軟件的開發質量單元測試作為一整套測試策略的基礎必須是全面的且要求易於建立和執行迅速然而對執行環境和被測試類外部代碼的依賴性使我們實現這些目標變得更為復雜例如把應用程序發布到容器將顯著地延長代碼和測試的周期而對其它類的依賴性通常也會導致測試的建立更加復雜和測試運行速度更為緩慢
集成兩個流行的測試框架(StrutsTestCase和EasyMock)來單元測試Struts應用程序將會更為容易地建立測試並加快測試速度然而這兩個框架之間尚存在一些隔閡從而很難把它們理想地集成到一起在本文中我將通過分析兩種方案(一個面向對象的方案和一個面向方面的方案)來探討這個問題同時我還將展示面向方面編程(AOP)是如何通過簡化一些看起來很困難的問題的解決方案而進一步補充面向對象編程(OOP)的
二集成需要
一個典型的Struts應用程序既能夠展示也其所使用的執行環境也會體現出類之間的依賴性問題這是因為Struts行為(Action)是在一個servlet容器內執行的並且典型情況下會調用其它的類來處理請求模擬對象測試方法有助於消除其中不必要的依賴性借助於繼承自基本JUnit測試集的MockStrutsTestCase類StrutsTestCase測試框架提供了對servlet容器的一種模擬實現這顯然方便了容器外測試因而也相應地加快了單元測試周期另一方面另一個測試框架—EasyMock—進一步便利了對協作類的動態模擬(Mock)這個框架中所提供的模擬能夠用更簡單的實現來代替真正的類並且添加了校驗邏輯以支持單元測試
非常清楚把這兩個框架結合在一起是非常有益的—Struts應用程序便可以在非常真實的隔離環境下進行測試理想情況下你需要使用下列步驟來實現這樣的一個單元測試
建立MockStrutsTestCase以便模擬servlet容器
借助於EasyMock來模擬行為所依賴的類
設置模擬的期望值
把模擬注入到當前測試的行為中
繼續進行測試和校驗
注意上面步驟中所執行的依賴性注入使被測試的Struts行為遠離了其真實的協作者而與一個模擬的行為進行交互為了把通過EasyMock生成的模擬注入到行為中你需要從測試類內部存取這些行為相應的實例遺憾的是這裡出現了一種障礙因為我們無法輕易地從MockStrutsTestCase中實現這樣的存取
三OOP方案
那麼你該如何從MockStrutsTestCase中存取行為實例呢?首先讓我們來分析一下MockStrutsTestCase和Struts的控制器組件之間的關系
圖中展示的關鍵關系有可能潛在地導致一種解決上面問題的方案
圖此處展示的關系能夠建立一種OOP方案
MockStrutsTestCase中提供了一個public類型的getter方法用於檢索ActionServlet
ActionServlet有一個protected類型的getter方法用於實現RequestProcessor
RequestProcessor把行為實例存儲為一個protected類型的成員
你是否可以子類化ActionServlet和RequestProcessor從而使MockStrutsTestCase能夠存取行為呢?相應的結果調用鏈看上去應該如下所示
注意在你分析完把MockStrutsTestCase鏈接到Struts行為的調用序列圖之後你就會發現此方法是行不通的
圖展示了存在於MockStrutsTestCase和Struts組件之間的關鍵性交互
圖存在於MockStrutsTestCase和Struts組件之間的交互
圖展示的問題涉及到Struts行為創建的時序問題到行為內部的模擬注入必須在調用MockStrutsTestCaseactionPerform()之前發生然而此時這些行為還不可用因為只有在調用actionPerform()後Requ
estProcessor才能夠創建這些行為實例
既然你不能很容易地把行為實例傳播到MockStrutsTestCase中那麼為什麼不子類化RequestProcessor並重載processActionCreate()方法呢?在這個重載方法中你可以存取所有的行為實例這樣以來創建配置和設置對相應行為實例的一個模擬一下子變得非常直接因為應該在執行完actionPerform()之後調用MockControlverify()方法所以你還需要重載processActionPerform()以進行此校驗調用
這種方案對於測試正規的Struts應用程序是不太適合的因為即使所有的行為僅與單個模擬進行交互測試一個行為也有可能要求多個測試方法—每個方法都具有不同的模擬期望為此我們建議的方案是創建不同的RequestProcessor子類相應於每個子類設置不同的模擬期望另外還需要多個Struts配置文件來指定不同的RequestProcessor子類最終管理大量的測試將成為一件令人頭疼的事情
四AOP方案
因此我們非常希望在執行某行為之前能夠通過某種方式實現在MockStrutsTestCase中使用該行為的實例如果你熟悉AOP那麼你會立即意識到它所提供的簡單方案即能直接滿足這一要求注意這裡的關鍵是定義一個切點由它負責捕獲行為執行連接點然後通過一個before advice把模擬注入到相應的行為中
在此我選擇使用AspectJ框架來實現這一方案當然其它的例如Spring AOP這樣的AOP實現也應該能夠良好工作不過Spring AOP還需要一個額外的步驟—通過Spring框架中的DelegatingActionProxy類把對Struts行為的管理委托給Spring
圖展示了基於AOP方案的單元測試示例靜態模型
圖基於AOP方案的單元測試示例靜態模型
SimpleAction是一個Struts行為的子類同時與ActionService進行協作其中SimpleActionTest派生於MockStrutsTestCase用來測試SimpleAction
SimpleActionTest使用EasyMock創建和建立一個模擬ActionServiceSimpleActionTest還實現StrutsActionPreExecuteListener接口以便在即將運行 SimpleAction的execute方法時接收通知作為通知的一部分SimpleActionTest接收SimpleAction實例以便注入ActionService模擬由方面類StrutsActionPreExecuteNotifier負責通知任何實現監聽器接口的測試類並且使相應的行為實例可用
下面的步驟描述了實現StrutsActionPreExecuteNotifier的過程
◆首先由一個切點選擇相應的測試方法執行連接點另一方面這個測試方法駐留於負責監聽該行為的預執行事件的測試類中另外這個切點還會暴露當前執行的測試類對象 pointcut mockStrutsTest(StrutsActionPreExecuteListener actionTest):
◆然後由第二個切點負責捕獲上面的行為執行連接點通過結合第一個切點匹配范圍被限制到該行為相應的測試方法的調用流程的內部這種進一步縮小的范圍對行為執行(並非通過測試方法激活)起到過濾作用最終方面根本不會影響到最後生成的代碼該行為及其相應的測試類實例都是經由切點參數加以暴露的 pointcut strutsActionExecute(Action action StrutsActionPreExecuteListener actionTest):
◆最後由一個與前一個切點相關聯的before advice負責通知測試類(它們擔任行為事件的監聽器)並且傳遞相應於模擬注入的行為實例
圖展示了這些類之間的動態交互情形
圖類之間的動態交互
注意圖中從行為到方面的虛線描述了對行為執行連接點的捕獲情況此時序圖與第一個時序圖比較其重要區別正在於行為執行之前發生的三個步驟
一個切點捕獲行為執行連接點(由從SimpleAction指向StrutsActionPreExecuteNotifier的虛線箭頭指出)
方面的before advice負責通知測試類並且把相應的行為實例傳遞給它
測試類把模擬對象注入到即將要開始執行的行為實例中
現在你可以基於前面概括的五個步驟繼續編寫行為測試下面的代碼展示了相應於SimpleActionTest的部分代碼步驟已在注釋中標出
使用MockStrutsTestCase和EasyMock進行行為測試的部分代碼
在行動及其依賴的服務之間存在四種可能的復合關系
每個行為依賴於一個服務
每個行為依賴於多個服務
多個行為依賴於一個服務
多個行為依賴於多個服務
我在此展示的方案能夠比較靈活而且相對容易地支持上面所有這四種情形因為模擬創建期望值建立以及模擬注入都能夠在單個的測試類內實現
你能夠不借助於監聽器接口就可以在StrutsActionPreExecuteNotifier內部模擬注入嗎?這看起來似乎使得測試類實現更簡單一些然而實踐證明類似早些時候討論的OOP方案編寫多個方面以創建不同的模擬對象並建立相應的不同的模擬期望是非常必要的另外在單個測試類內本地化模擬的創建與安裝(借助於監聽技術這是可能的)將變得更為方便
五總結
對於我們在本文中所討論的集成問題有人可能會創造出一套相當不錯的OOP方案然而構造這種方案很可能需要對Struts和StrutsTestCase有深入的理解才行並且要付出相當的努力影響本文中所討論的兩個測試框架(StrutsTestCase和EasyMock)緊密集成的主要障礙在於在Struts行為實例執行之前很難實現對它的訪問在認識了導致這種障礙的基本原因之後AOP方案自然地出現在我們面前不必再強求於基於傳統型OOP的那種更復雜的方案AOP允許我們把我們的方案更為緊密地映射到問題空間
其實AOP的真正魔術在於它的連接點模型它能夠使你穿越中間對象(例如ActionServlet和RequestProcessor)進而直指問題的核心借助於AOP技術中確定橫切關注點這種非常節儉的方法開發者即能夠設計出非常直觀而且更為簡單的解決方案AOP這種強有力的編程方法正好彌補了傳統型OOP編程中所存在的不足如果被恰當用於解決適當類型的問題那麼AOP有助於改進代碼的模塊化最終會產生出更為清晰和更易於理解的代碼最後非常希望本文不僅有助於你的Struts應用程序的單元測試而且還吸引你進一步探討AOP編程所體現出來的其它重要優點
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28045.html