雖然 Struts 正在慢慢退出 Web 框架的歷史舞臺,但它的遺產仍然存在,存在的形式主要是需要測試和維護的應用程序。這個月,Andrew Glover 向您介紹如何使用 JUnit 的 StrutsTestCase、DbUnit 以及在這個系列中迄今為止學到的一些工具,把以質量為中心的方法用于 Struts 上的測試(可以這么說)。
基于 Java™ 的 Web 開發領域最近出現了豐富的競爭性技術。啟動新項目的開發人員可以在許多不同的框架之間進行選擇,包括 JavaServer Faces、Tapestry、Shale、Grails 和 Seam (只列舉眾多機靈的名稱中的幾個)。很快,我們就可以通過 JRuby 框架在 Java 編程中使用 Ruby on Rails 了!
但就在不遠的過去,只有一個 Java Web 開發框架卓然而立。Struts 是第一個在 Java 世界掀起風暴的框架,而且多年以來,好像是如果一個項目不用 Struts 構建就沒有前途一樣。沒有 Struts 經驗的 Java 開發人員很稀少,也很不幸,就像今天的開發人員沒有聽說過 Ruby on Rails 一樣。
![]() |
|
即使 Struts 正慢慢地從舞臺中央退去(原來的基本框架,現在叫做 Struts 1,似乎正在退出 Web 框架的歷史舞臺),但它的遺產仍然存在,既以 Shale (請參閱 參考資料)的形式存在,又以運行在世界各地的成千上萬的遺留應用程序的形式存在。因為許多企業寧愿測試和維護這些應用程序而不愿意花錢重新編寫它們,所以理解 Struts 應用程序的一些缺陷,以及如何圍繞它們進行重構,是個好主意。
這個月,我要把以質量為核心的方法用于 Struts 應用程序的測試場景。結合現實,這個場景圍繞著最普遍的 Struts 構造:深受喜愛的 Action
類。
Struts 的革新之一就是把 Web 開發從 Servlet 移進了 Action
類。這些類包含業務邏輯,以 JavaBean 的形式(通常叫做 ActionForm
)把數據傳送到 JSP。然后 JSP 處理應用程序視圖。Struts 到 MVC 的方法非常容易掌握,以至于許多開發團隊冒失地闖進去,而很少考慮與 Action
相關的長期設計和維護問題。
![]() |
|
雖然在那個時候(過去的自由時光。┛赡軟]人想過,但 Struts Action
類通常成為復雜性的保護所。像在老的 EJB 架構中聲名狼籍的會話 Facade 一樣,Action
類會成為特定業務過程的嚴格偽裝,或者通過直接調用 EJB,通過打開數據庫連接,或者通過調用其他高度依賴的對象。Action
類還有輸出耦合(通過 java.servlet
API 包中的對象,例如 HttpServletRequest
和 HttpServletResponse
),從而極難把它們隔離出來測試。
隔離出來測試 Action
類的困難意味著它們可以很容易變得相當復雜 —— 特別是當它們變成越來越深入地與遺留框架耦合的時候,F在我們來看這個困難在真實的遺留應用程序場景中作用的情況。
即使最簡單的 Struts Action
類也會是個測試挑戰。例如,以清單 1 中的 execute()
方法為例;它看起來足夠簡單,可以測試,但是真的么?
清單 1. 這個方法看起來容易測試……
public ActionForward execute(ActionMapping mapping, ActionForm aForm, HttpServletRequest req, HttpServletResponse res) throws Exception { try{ String newPassword = ((ChangePasswordForm)aForm).getNewPassword1(); String username = ((ChangePasswordForm)aForm).getUsername(); IUser user = DataAccessUtils.getDaos().getUserDao().findUserByUsername(username); user.digestAndSetPassword(newPassword); DataAccessUtils.getDaos().getUserDao().saveUser(user); }catch(Throwable thr){ return findFailure(mapping, aForm, req, res); } return findSuccess(mapping, aForm, req, res); } |
圖 1. Action 類的輸出耦合

但是,就像在圖 1 中可以看到的,在試圖隔離 ChangePasswordAction
類并檢驗 execute()
方法時,該類給出了一些有代表性的挑戰。為了有效地測試 execute()
方法,必須處理三層耦合。首先,到 Struts 自身的耦合;其次,Servlet API 代表一個障礙;最后,到業務對象包的耦合,進一步檢查業務對象包,還會有數據訪問層使用 Hibernate 和 Spring。
![]() |
|
對于更高的復雜性,請注意 清單 1 中的代碼如何把 aForm
參數轉換成 ChangePasswordForm
對象,它是 Struts ActionForm
類型。這些 JavaBeans 有一個 validate
方法,這個方法由 Struts 在調用 Action
類的 execute()
方法之前調用。
在清單 2 中,可以看到所有這個復雜性會在哪里發生。ChangePasswordForm
的 validate()
方法的代碼片段演示了保證兩個屬性(newPassword1
和 newPassword2
)不為空并彼此相等的簡單邏輯。但是,如果 Struts 發現 errors
集合(類型為 ActionErrors
)包含一些 ActionError
對象,就會沿著錯誤路徑走,例如帶著出錯消息重新顯示 Web 頁面。
清單 2. ChangePasswordForm 的驗證邏輯
if((newPassword1 == null) || (newPassword1.length() < 1)) { errors.add("newPassword1", new ActionError("error.changePassword.newPassword1Required"));}if((newPassword2 == null) || (newPassword2.length() < 1)) { errors.add("newPassword2", new ActionError("error.changePassword.newPassword2Required"));}if((newPassword1 != null) && (newPassword2 != null)) { if(!newPassword1.equals(newPassword2)) { errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.changePassword.passwordsDontMatch")); }} |
清單 1 和 清單 2 的代碼不特殊也不特定于某個領域。它是無數應用程序中都包含的簡單口令修改邏輯。如果正在測試 Struts 遺留應用程序,將不得不花些時間處理口令邏輯,但是如何用可重復的方式測試它呢?
文章來源于領測軟件測試網 http://www.kjueaiud.com/