歡迎您光臨本站 註冊首頁

進行Struts應用程序單元測試開發

←手機掃碼閱讀     火星人 @ 2014-03-10 , reply:0

一、引言

測試驅動開發在減少開發努力的同時也改進了軟體的開發質量.單元測試,作為一整套測試策略的基礎,必須是全面的,且要求易於建立和執行迅速.然而,對執行環境和被測試類外部代碼的依賴性使我們實現這些目標變得更為複雜.例如,把應用程序發布到容器將顯著地延長代碼和測試的周期;而對其它類的依賴性通常也會導致測試的建立更加複雜和測試運行速度更為緩慢.

集成兩個流行的測試框架(StrutsTestCase和EasyMock)來單元測試Struts應用程序將會更為容易地建立測試並加快測試速度.然而,這兩個框架之間尚存在一些「隔閡」,從而很難把它們理想地集成到一起.在本文中,我將通過分析兩種方案(一個面向對象的方案和一個面向方面的方案)來探討這個問題.同時,我還將展示面向方面編程(AOP)是如何通過簡化一些看起來很困難的問題的解決方案而進一步補充面向對象編程(OOP)的.

二、集成需要

一個典型的Struts應用程序既能夠展示也其所使用的執行環境也會體現出類之間的依賴性問題;這是Struts行為(Action)是在一個servlet容器內執行的,並且典型情況下會調用其它的類來處理請求.模擬對象測試方法有助於消除其中不必要的依賴性.藉助於繼承自基本JUnit測試集的MockStrutsTestCase類,StrutsTestCase測試框架提供了對servlet容器的一種模擬實現.這顯然方便了容器外測試,因而也相應地加快了單元測試周期.另一方面,另一個測試框架—EasyMock—進一步便利了對協作類的動態模擬(Mock).這個框架中所提供的模擬能夠用更簡單的實現來代替真正的類,並且添加了校驗邏輯以支持單元測試.

非常清楚,把這兩個框架結合在一起是非常有益的—Struts應用程序便可以在非常真實的隔離環境下進行測試.理想情況下,你需要使用下列步驟來實現這樣的一個單元測試:

1.建立MockStrutsTestCase以便模擬servlet容器.

2.藉助於EasyMock來模擬行為所依賴的類.

3.設置模擬的期望值.

4.把模擬注入到當前測試的行為中.

5.繼續進行測試和校驗.

注意,上面步驟4中所執行的依賴性注入使被測試的Struts行為遠離了其真實的協作者而與一個模擬的行為進行交互.為了把通過EasyMock生成的模擬注入到行為中,你需要從測試類內部存取這些行為相應的實例.遺憾的是,這裡出現了一種障礙,我們無法輕易地從MockStrutsTestCase中實現這樣的存取.

三、OOP方案

那麼,你該如何從MockStrutsTestCase中存取行為實例呢?,讓我們來分析一下MockStrutsTestCase和Struts的控制器組件之間的關係.


圖1中展示的關鍵關係有可能潛在地導致一種解決上面問題的方案.


圖1:此處展示的關係能夠建立一種OOP方案

.MockStrutsTestCase中提供了一個public類型的getter方法用於檢索ActionServlet.

.ActionServlet有一個protected類型的getter方法用於實現RequestProcessor.

.RequestProcessor把行為實例存儲為一個protected類型的成員.

你是否可以子類化ActionServlet和RequestProcessor從而使MockStrutsTestCase能夠存取行為呢?相應的結果調用鏈看上去應該如下所示:

注意,在你分析完把MockStrutsTestCase鏈接到Struts行為的調用序列圖之後,你就會發現此方法是行不通的.

圖2展示了存在於MockStrutsTestCase和Struts組件之間的關鍵性交互.

圖2:存在於MockStrutsTestCase和Struts組件之間的交互

圖2展示的問題涉及到Struts行為創建的時序問題.到行為內部的模擬注入必須在調用MockStrutsTestCase.actionPerform()之前發生.然而,此時這些行為還不可用,只有在調用actionPerform()后,Requ

estProcessor才能夠創建這些行為實例.

既然你不能很容易地把行為實例傳播到MockStrutsTestCase中,那麼,為什麼不子類化RequestProcessor並重載processActionCreate()方法呢?在這個重載方法中,你可以存取所有的行為實例;這樣以來,創建、配置和設置對相應行為實例的一個模擬一下子變得非常直接.應該在執行完actionPerform()之後調用MockControl.verify()方法,,你還需要重載processActionPerform()以進行此校驗調用.

這種方案對於測試正規的Struts應用程序是不太適合的.即使所有的行為僅與單個模擬進行交互,測試一個行為也有可能要求多個測試方法—每個方法都具有不同的模擬期望.為此,我們建議的方案是:創建不同的RequestProcessor子類,相應於每個子類設置不同的模擬期望.另外,還需要多個Struts配置文件來指定不同的RequestProcessor子類.最終,管理大量的測試將成為一件令人頭疼的事情.

四、AOP方案


因此,我們非常希望,在執行某行為之前能夠通過某種方式實現在MockStrutsTestCase中使用該行為的實例.如果你熟悉AOP,那麼,你會立即意識到它所提供的簡單方案即能直接滿足這一要求.注意:這裡的關鍵是定義一個切點,由它負責捕獲行為執行連接點;然後通過一個before advice把模擬注入到相應的行為中.

在此,我選擇使用AspectJ框架來實現這一方案.當然,其它的例如Spring AOP這樣的AOP實現也應該能夠良好工作.不過,Spring AOP還需要一個額外的步驟—通過Spring框架中的DelegatingActionProxy類把對Struts行為的管理委託給Spring.

圖3展示了基於AOP方案的單元測試示例靜態模型.


圖3:基於AOP方案的單元測試示例靜態模型

SimpleAction是一個Struts行為的子類,同時與ActionService進行協作.其中,SimpleActionTest派生於MockStrutsTestCase,用來測試SimpleAction.

SimpleActionTest使用EasyMock創建和建立一個模擬ActionService.SimpleActionTest還實現StrutsActionPreExecuteListener介面以便在即將運行 SimpleAction的execute方法時接收通知.作為通知的一部分,SimpleActionTest接收SimpleAction實例以便注入ActionService模擬.由方面類StrutsActionPreExecuteNotifier負責通知任何實現JianTingQi介面的測試類,並且使相應的行為實例可用.

下面的步驟描述了實現StrutsActionPreExecuteNotifier的過程:

,由一個切點選擇相應的測試方法執行連接點.另一方面,這個測試方法駐留於負責監聽該行為的預執行事件的測試類中.另外,這個切點還會暴露當前執行的測試類對象: pointcut mockStrutsTest(StrutsActionPreExecuteListener actionTest):


◆然後,由第二個切點負責捕獲上面的行為執行連接點.通過結合第一個切點,匹配範圍被限制到該行為相應的測試方法的調用流程的內部.這種進一步縮小的範圍對行為執行(並非通過測試方法激活)起到過濾作用.最終,方面根本不會影響到生成的代碼.該行為及其相應的測試類實例都是經由切點參數加以暴露的: pointcut strutsActionExecute(Action action, StrutsActionPreExecuteListener actionTest):


,由一個與前一個切點相關聯的before advice負責通知測試類(它們擔任行為事件的JianTingQi)並且傳遞相應於模擬注入的行為實例:


圖4展示了這些類之間的動態交互情形.

圖4:類之間的動態交互

注意,圖中從行為到方面的虛線描述了對行為執行連接點的捕獲情況.此時序圖與第一個時序圖比較,其重要區別正在於行為執行之前發生的三個步驟:

1.一個切點捕獲行為執行連接點(由從SimpleAction指向StrutsActionPreExecuteNotifier的虛線箭頭指出).

2.方面的before advice負責通知測試類並且把相應的行為實例傳遞給它.

3.測試類把模擬對象注入到即將要開始執行的行為實例中.

現在,你可以基於前面概括的五個步驟繼續編寫行為測試.下面的代碼展示了相應於SimpleActionTest的部分代碼,步驟已在註釋中標出.

使用MockStrutsTestCase和EasyMock進行行為測試的部分代碼:

在行動及其依賴的服務之間存在四種可能的複合關係:

每個行為依賴於一個服務.

每個行為依賴於多個服務.

多個行為依賴於一個服務.

多個行為依賴於多個服務.

我在此展示的方案能夠比較靈活相對容易地支持上面所有這四種情形,模擬創建、期望值建立以及模擬注入都能夠在單個的測試類內實現.

你能夠不藉助於JianTingQi介面就可以在StrutsActionPreExecuteNotifier內部模擬注入嗎?這看起來似乎是的測試類實現更簡單一些.然而,實踐證明,類似早些時候討論的OOP方案,編寫多個方面以創建不同的模擬對象並建立相應的不同的模擬期望是非常必要的.另外,在單個測試類內本地化模擬的創建與安裝(藉助於監聽技術,這是可能的)將變得更為方便.

五、總結

對於我們在本文中所討論的集成問題,有人可能會創造出一套相當不錯的OOP方案.然而,構造這種方案很可能需要對Struts和StrutsTestCase有深入的理解才行,並且要付出相當的努力.影響本文中所討論的兩個測試框架(StrutsTestCase和EasyMock)緊密集成的主要障礙在於,在Struts行為實例執行之前很難實現對它的訪問.在認識了導致這種障礙的基本原因之後,AOP方案自然地出現在我們面前.不必再強求於基於傳統型OOP的那種更複雜的方案,AOP允許我們把我們的方案更為緊密地映射到問題空間.

其實,AOP的真正「魔術」在於它的連接點模型,它能夠使你「穿越」中間對象(例如ActionServlet和RequestProcessor)進而直指問題的核心.藉助於AOP技術中確定橫切關注點這種非常「節儉」的方法,開發者即能夠設計出非常直觀更為簡單的解決方案.AOP,這種強有力的編程方法正好彌補了傳統型OOP編程中所存在的不足.如果被恰當用於解決適當類型的問題,那麼,AOP有助於改進代碼的模塊化,最終會產生出更為清晰和更易於理解的代碼.,非常希望本文不僅有助於你的Struts應用程序的單元測試,還吸引你進一步探討AOP編程所體現出來的其它重要優點.


[火星人 ] 進行Struts應用程序單元測試開發已經有162次圍觀

http://coctec.com/docs/java/show-post-61539.html