您的位置:軟件測(cè)試 > 開(kāi)源軟件測(cè)試 > 開(kāi)源單元測(cè)試工具 >
測(cè)試Struts遺留的應(yīng)用程序
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2013/2/1 16:06:18 ] 推薦標(biāo)簽:

雖然 Struts 正在慢慢退出 Web 框架的歷史舞臺(tái),但它的遺產(chǎn)仍然存在,存在的形式主要是需要測(cè)試和維護(hù)的應(yīng)用程序。這個(gè)月,Andrew Glover 向您介紹如何使用 JUnit 的 StrutsTestCase、DbUnit 以及在這個(gè)系列中迄今為止學(xué)到的一些工具,把以質(zhì)量為中心的方法用于 Struts 上的測(cè)試(可以這么說(shuō))。

基于 Java™ 的 Web 開(kāi)發(fā)領(lǐng)域近出現(xiàn)了豐富的競(jìng)爭(zhēng)性技術(shù)。啟動(dòng)新項(xiàng)目的開(kāi)發(fā)人員可以在許多不同的框架之間進(jìn)行選擇,包括 JavaServer Faces、Tapestry、Shale、Grails 和 Seam (只列舉眾多機(jī)靈的名稱中的幾個(gè))。很快,我們可以通過(guò) JRuby 框架在 Java 編程中使用 Ruby on Rails 了!

但在不遠(yuǎn)的過(guò)去,只有一個(gè) Java Web 開(kāi)發(fā)框架卓然而立。Struts 是第一個(gè)在 Java 世界掀起風(fēng)暴的框架,而且多年以來(lái),好像是如果一個(gè)項(xiàng)目不用 Struts 構(gòu)建沒(méi)有前途一樣。沒(méi)有 Struts 經(jīng)驗(yàn)的 Java 開(kāi)發(fā)人員很稀少,也很不幸,像的開(kāi)發(fā)人員沒(méi)有聽(tīng)說(shuō)過(guò) Ruby on Rails 一樣。
 
提高代碼質(zhì)量
不要錯(cuò)過(guò) Andrew 的附帶 討論組 ,可以得到迫切問(wèn)題的答案。

即使 Struts 正慢慢地從舞臺(tái)中央退去(原來(lái)的基本框架,現(xiàn)在叫做 Struts 1,似乎正在退出 Web 框架的歷史舞臺(tái)),但它的遺產(chǎn)仍然存在,既以 Shale (請(qǐng)參閱 參考資料)的形式存在,又以運(yùn)行在世界各地的成千上萬(wàn)的遺留應(yīng)用程序的形式存在。因?yàn)樵S多企業(yè)寧愿測(cè)試和維護(hù)這些應(yīng)用程序而不愿意花錢(qián)重新編寫(xiě)它們,所以理解 Struts 應(yīng)用程序的一些缺陷,以及如何圍繞它們進(jìn)行重構(gòu),是個(gè)好主意。

這個(gè)月,我要把以質(zhì)量為核心的方法用于 Struts 應(yīng)用程序的測(cè)試場(chǎng)景。結(jié)合現(xiàn)實(shí),這個(gè)場(chǎng)景圍繞著普遍的 Struts 構(gòu)造:深受喜愛(ài)的 Action 類(lèi)。

1、2、3,行動(dòng)!

Struts 的革新之一是把 Web 開(kāi)發(fā)從 Servlet 移進(jìn)了 Action 類(lèi)。這些類(lèi)包含業(yè)務(wù)邏輯,以 JavaBean 的形式(通常叫做 ActionForm)把數(shù)據(jù)傳送到 JSP。然后 JSP 處理應(yīng)用程序視圖。Struts 到 MVC 的方法非常容易掌握,以至于許多開(kāi)發(fā)團(tuán)隊(duì)冒失地闖進(jìn)去,而很少考慮與 Action 相關(guān)的長(zhǎng)期設(shè)計(jì)和維護(hù)問(wèn)題。

 
測(cè)試和復(fù)雜性

我已經(jīng)發(fā)現(xiàn),在開(kāi)發(fā)人員的測(cè)試和代碼的復(fù)雜性之間存在強(qiáng)烈的相關(guān)性:沒(méi)有其中一個(gè)的地方,通常也沒(méi)有另一個(gè)。高度復(fù)雜的編碼難于測(cè)試,結(jié)果是很少有人會(huì)真正為它編寫(xiě)測(cè)試。反過(guò)來(lái),編寫(xiě)測(cè)試可以降低代碼的復(fù)雜性。因?yàn)榻o復(fù)雜代碼編寫(xiě)測(cè)試更困難,而且因?yàn)闀?huì)邊走邊測(cè)試,所以會(huì)發(fā)現(xiàn)自己朝著更簡(jiǎn)單的代碼構(gòu)造前進(jìn)。如果代碼太復(fù)雜,而且知道不得不測(cè)試它,您可能會(huì)在測(cè)試之前對(duì)復(fù)雜性進(jìn)行重構(gòu)。不論如何看待,為不那么簡(jiǎn)單的代碼編寫(xiě)測(cè)試是消滅代碼復(fù)雜性的好實(shí)踐。

雖然在那個(gè)時(shí)候(過(guò)去的自由時(shí)光。┛赡軟](méi)人想過(guò),但 Struts Action 類(lèi)通常成為復(fù)雜性的保護(hù)所。像在老的 EJB 架構(gòu)中聲名狼籍的會(huì)話 Facade 一樣,Action 類(lèi)會(huì)成為特定業(yè)務(wù)過(guò)程的嚴(yán)格偽裝,或者通過(guò)直接調(diào)用 EJB,通過(guò)打開(kāi)數(shù)據(jù)庫(kù)連接,或者通過(guò)調(diào)用其他高度依賴的對(duì)象。Action 類(lèi)還有輸出耦合(通過(guò) java.servlet API 包中的對(duì)象,例如 HttpServletRequest 和 HttpServletResponse),從而極難把它們隔離出來(lái)測(cè)試。

隔離出來(lái)測(cè)試 Action 類(lèi)的困難意味著它們可以很容易變得相當(dāng)復(fù)雜 —— 特別是當(dāng)它們變成越來(lái)越深入地與遺留框架耦合的時(shí)候,F(xiàn)在我們來(lái)看這個(gè)困難在真實(shí)的遺留應(yīng)用程序場(chǎng)景中作用的情況。

測(cè)試挑戰(zhàn)

即使簡(jiǎn)單的 Struts Action 類(lèi)也會(huì)是個(gè)測(cè)試挑戰(zhàn)。例如,以清單 1 中的 execute() 方法為例;它看起來(lái)足夠簡(jiǎn)單,可以測(cè)試,但是真的么?

清單 1. 這個(gè)方法看起來(lái)容易測(cè)試……

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 = DataAclearcase/" target="_blank" >ccessUtils.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 類(lèi)的輸出耦合

但是,像在圖 1 中可以看到的,在試圖隔離 ChangePasswordAction 類(lèi)并檢驗(yàn) execute() 方法時(shí),該類(lèi)給出了一些有代表性的挑戰(zhàn)。為了有效地測(cè)試 execute() 方法,必須處理三層耦合。首先,到 Struts 自身的耦合;其次,Servlet API 代表一個(gè)障礙;后,到業(yè)務(wù)對(duì)象包的耦合,進(jìn)一步檢查業(yè)務(wù)對(duì)象包,還會(huì)有數(shù)據(jù)訪問(wèn)層使用 Hibernate 和 Spring。
 
每種情況一個(gè) mock?

即使在我編寫(xiě)本文時(shí),我還可以聽(tīng)到開(kāi)發(fā)人員的嘲笑者 認(rèn)為我的測(cè)試問(wèn)題通過(guò)明智地使用 mock 對(duì)象能輕易解決?梢 用 mock 對(duì)象創(chuàng)建一級(jí)隔離,它會(huì)形成更容易的測(cè)試;但是,我要說(shuō)的是,把目標(biāo)對(duì)象通過(guò) mock 排除所需要的付出級(jí)別,比起承認(rèn)隔離測(cè)試?yán)щy所需要的付出,要多得多。在這種情況下,我會(huì)采用在更高層次上的測(cè)試,這級(jí)測(cè)試有時(shí)叫做集成測(cè)試。

對(duì)于更高的復(fù)雜性,請(qǐng)注意 清單 1 中的代碼如何把 aForm 參數(shù)轉(zhuǎn)換成 ChangePasswordForm 對(duì)象,它是 Struts ActionForm 類(lèi)型。這些 JavaBeans 有一個(gè) validate 方法,這個(gè)方法由 Struts 在調(diào)用 Action 類(lèi)的 execute() 方法之前調(diào)用。

犯錯(cuò)誤太容易了

在清單 2 中,可以看到所有這個(gè)復(fù)雜性會(huì)在哪里發(fā)生。ChangePasswordForm 的 validate() 方法的代碼片段演示了保證兩個(gè)屬性(newPassword1 和 newPassword2)不為空并彼此相等的簡(jiǎn)單邏輯。但是,如果 Struts 發(fā)現(xiàn) errors 集合(類(lèi)型為 ActionErrors)包含一些 ActionError 對(duì)象,會(huì)沿著錯(cuò)誤路徑走,例如帶著出錯(cuò)消息重新顯示 Web 頁(yè)面。

清單 2. ChangePasswordForm 的驗(yàn)證邏輯

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 的代碼不特殊也不特定于某個(gè)領(lǐng)域。它是無(wú)數(shù)應(yīng)用程序中都包含的簡(jiǎn)單口令修改邏輯。如果正在測(cè)試 Struts 遺留應(yīng)用程序,將不得不花些時(shí)間處理口令邏輯,但是如何用可重復(fù)的方式測(cè)試它呢?

軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠(chéng)聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd