基于
Junit2.0
的
StrutsTestCase
應(yīng)用
?
??????
在我的前一篇文檔《測試驅(qū)動的開發(fā)是重要的》中說過我要寫一些測試框架應(yīng)用方面的文檔,今天我要實(shí)現(xiàn)我的諾言之一,這篇文章是介紹
StrutsTeseCase
的,熟悉并采用
struts
的開發(fā)員曾經(jīng)一定有過這樣一個困擾:我的
action
如何進(jìn)行測試?(不是說要“測試先行”么?),如果沒有一個可行的測試框架那我的
struts
環(huán)境去哪里模擬(方便的、透明的去模擬)?不要著急,接下來的部分我要向你們介紹這樣一種可以滿足我們要求的測試框架
:strutstestcae
。
??????
?????????????????????
????????????????????
????????????????????
?
——寫在前面
?
主要內(nèi)容介紹:
1.
?
StrutsTeseCase
是什么?
2.
?
它的“家”在哪里?
3.
?
如何讓它來為我們工作?(伴隨說明:我到底該實(shí)施“測試先行”?)
4.
?
兼容
struts1.1
開發(fā)員
5.
?
參考資源
?
“由于在這里沒有牽涉到
Struts
以及
Junit
入門的知識,所以我假定這篇文章的讀者都是有
struts
開發(fā)經(jīng)驗(yàn)的開發(fā)員并熟悉
Junit
?!?/span>
?
第一部分:
StrutsTestCase
是什么?
??????
?????? StrutsTestCase
是基于
Junit
的一個方便測試
struts
框架的測試框架。它提供模擬對象(
Mock Object
)和
Cactus
兩種方式來“真實(shí)”的運(yùn)行
Struts ActionServlet
,它允許你在不啟動
servlet
引擎的情況下測試你的
struts
代碼。因?yàn)?/span>
strutstestcase
可以用
ActionServlet
來測試你的代碼,所以它不光可以測試你的
action
,同時它也可以測試你的(容器中的?)
mapping,frombeans
以及
forwards
聲明。我前面曾提到過它對我們開發(fā)員來說是“透明的”,因?yàn)橄?/span>
action,mapping,form beans
以及
forward
等等,我們真的可以象在常規(guī)的
XXXAction
中一樣在我們的測試代碼中隨意的使用它們。
?
??????
在最新的版本中它還提供了對
tiles
和多模塊(
struts1.1
中的功能)的測試。
?
??????
哇,是不是很奇妙,不要著急,我們很快就可以領(lǐng)略到的它的妙處。
?
第二部分:它的“家”在哪里?
?
??????
就象許許多多的開源項(xiàng)目一樣,
StrutsTestCase
的家也在“
sourceforge.org
”(我們偉大的
sourceforge
就象一個繁忙的峰槽一樣
J
),你可以通過
http://sourceforge.net/project/showfiles.php?group_id=39190
來下載它得最新版本。
?????? JavaDoc: http://strutstestcase.sourceforge.net/api/index.html
??????
熱點(diǎn)論壇:
http://sourceforge.net/forum/forum.php?forum_id=121751
??????
常見問題:
http://strutstestcase.sourceforge.net/faq.htm
?
第二部分:如何讓它來為我們工作?
?
“模仿測試(
Mock Testing
)
VS
容器內(nèi)測試(
In-Container Testing
)”
?
?
???
通常測試服務(wù)器端代碼有兩種比較常用的測試方法:
?
?
???
模仿對象(
mock objects
)它通過假設(shè)服務(wù)器端容器來達(dá)到測試效果;
?
???
容器內(nèi)測試(
in-container testing
)
,
它則是在真實(shí)的容器內(nèi)達(dá)到測試效果;
?
?
???
而我們的
StrutsTestCase
則在對你的測試代碼最小影響下能分別扮演上邊兩種角色。因此我們不得不說到它的這兩種實(shí)現(xiàn)是如何完成的?
?
?????? StrutsTestCase
提供兩種基類(他們分別繼承標(biāo)準(zhǔn)的
Junit TestCase
):
?
?????? MockStrutsTestCase:
?????????????
通過名字也可以知道他是通過第一中方法在不啟動
servlet
的條件下來模仿一些
HttpServlet
實(shí)現(xiàn)假設(shè)容器環(huán)境的。
?????? CactusStrutsTestCase:
?????????????
它是體現(xiàn)在容器內(nèi)測試(真實(shí)環(huán)境測試)的,其通過另外一種測試框架(
Cactus testing framework
:
http://jakarta.apache.org/cactus
)
struts
代碼。
??????
??????
Ps:
本文中牽涉的代碼都是通過第一中方法(繼承
MockStrutsTestCase
)來完成測試的,要想用
CactusStrutsTeseCase
你只要簡單的讓測試代碼繼承
CactusStrutsTeseCase
即可。
?
下面我們著重講解
MockStrutsTestCase
是為我們工作的?
?
首先我們先看看一個簡單的
LoginAction
的簡化代碼:
?
public class LoginAction extends Action {
?
??? public ActionForward perform(ActionMapping mapping,
???????????????????????????????? ActionForm form,
???????????????????????????????? HttpServletRequest request,
???????????????????????????????? HttpServletResponse response)
??? {
?
???
????String username = ((LoginForm) form).getUsername();
??????? String password = ((LoginForm) form).getPassword();
?
??????? ActionErrors errors = new ActionErrors();
?
??????? if ((!username.equals("Jplateau")) || (!password.equals("sandy")))
??????????? errors.add("password",new ActionError("error.password.mismatch"));
?
??????? if (!errors.empty()) {
??????????? saveErrors(request,errors);
??????????? return mapping.findForward("login");
??????? }
?
??????? // store authentication info on the session
?????
??HttpSession session = request.getSession();
??????? session.setAttribute("authentication", username);
?
??????? // Forward control to the specified success URI
??????? return mapping.findForward("success");
?
}
|
?
上邊
LoginAction
完成一個簡單的登陸意圖,從
client
搜集登陸數(shù)據(jù)(用戶名和密碼),然后做一個驗(yàn)證,如果驗(yàn)證有誤返回登陸頁;如果登陸成功返回成功頁(或業(yè)務(wù)工作平臺)并把用戶姓名放入
session
。
?
那我們就從上邊這個簡單的程序入手:
?
首先,我們應(yīng)該創(chuàng)建一個測試用例
TestLoginAction
,其基本架子是這樣的:
(請記住此時上邊
LoginAction
的代碼你還沒有寫,并且
struts_config.xml
中的關(guān)于
LoginAction
的
actionmapping
也是沒有的,這些東西我們要經(jīng)過邊測試邊寫,但一定是先寫測試,天啊,什么都還沒有我該怎樣測試啊,不要急,且看下去,
J
)
?
public class TestLoginAction extends MockStrutsTestCase {
?
??? public void setUp() { super.setUp(); }
?
??? public void tearDown() { super.tearDown(); }
?
??? public TestLoginAction(String testName) { super(testName); }
?
??? public void testSuccessfulLogin() {}
}
|
?
首先我們頭腦總中有這樣一個
actionmapping(
注意只是假設(shè)的
)
:
?
<action path=”/longin” type=”Jplateau.strutstestcase.LonginAction”
?????????????????? scope="request"
?????????????????? name="userForm">
<forward? name="success" path="/main.jsp"/>
<forward? name="login" path="/login.jsp"/>
</action>
|
?
有了這樣一個假設(shè),我們就可以從測試代碼入手:
public class TestLoginAction extends MockStrutsTestCase {
?
public void setUp() {
?super.setUp();
?//
這里做一些初始化的東西,譬如數(shù)據(jù)庫連接等
?}
?
public void tearDown() {
?super.tearDown();
?//
這里關(guān)閉你在
setup
中開啟的資源,如關(guān)閉數(shù)據(jù)庫連接等
}
?
??? public TestLoginAction(String testName) { super(testName); }
?
public void testSuccessfulLogin() {
?
//
選擇你要執(zhí)行哪一個
actionmapping?
這里就用我們剛才做的假設(shè)材料
this. setRequestPathInfo("/login");
?
//
首先要初始化提交數(shù)據(jù):用戶名、密碼
this.addRequestParameter(“usrename”,”Jplateau”);
this.addRequestParameter(“passwd”,”sandy”);
//
注意有了這個之后,你就可以從
formbean
中使用提交以后的用戶名和密碼數(shù)據(jù)了,下
//
面我會解釋
?
//
好,初始化數(shù)據(jù)完成以后開始執(zhí)行
action
中的
execute(),
很簡單,掉用
actionPerform()
this.
ActionPerform();
?
//
嚴(yán)正返回是否正確?這里就用我們剛才做的假設(shè)材料
this verifyForward(“success”);
.
?
//
下面驗(yàn)證登陸成功以后
session
中是否有用戶的名稱?
String expect_username_from_session=”Jplateau”;
//
這里需要插寫內(nèi)容,就是
strutstestcase
完全可以在測試代碼中使用跟真實(shí)
action
//
中一樣的環(huán)境,譬如可以通過
this.getActionForm()
得到相應(yīng)的
ActionForm
//
可以通過
this.getRequest()
得到
HttpRequest
//
可以通過
this.getSession()
得到
HttpSession,
如下:
String actual_username_from_session=this.getSession().getAttribute(“authentication”);
?
this
. assertEquals(“”, expect_username_from_session, actual_username_from_session);
}
?
public void testFailureLogin(){
//
登陸失敗的測試在此就不寫了
?
}
}
|
?
?
上邊是一個簡單的測試代碼,好,運(yùn)行!他此時肯定是通不過的,首先我們的
java
代碼還沒有寫,
struts-config.xml
還沒有配,那么,現(xiàn)在你可以現(xiàn)在做這些事情:“用最簡單的做法或代碼讓上邊那個測試通過。”
?
第三部分:兼容
struts1.1
開發(fā)員
?
下面說寫和
struts1.1
相關(guān)的內(nèi)容:測試
tiles
和多模塊!
?
測試
tiles
假設(shè)我們的
actionmapping
中有這樣
forward
到
tiles
的情況,如下:
?
//
這里有關(guān)
struts
中
tiles
的使用不做介紹,請參考相關(guān)資料
<forwardname="success"?????????????????? ? path="/userListLayout"/>
|
?
同時
tiles_defs.xml
中有相應(yīng)配置:
?
//
其中
mainLayout
是我在
tiles
定義的類型框架
??
<definition name="userListLayout" extends="mainLayout">
????? <put name="body" value="/main.jsp" />
? </definition>
|
?
?
那么我們在測試代碼中可以如下測試
tiles
//
this. verifyTilesForward(“success”,”userListLayout”);
|
?
測試多模塊:
?
關(guān)于多模塊的測試我希望能在下面代碼的注釋部分給你一個大概的介紹:
?
public class TestLoginAction extends MockStrutsTestCase {
?
??? public TestLoginAction(String testName) { super(testName); }
?
public void testSuccessfulLogin() {
?? //
“
mymodule
”為系統(tǒng)中某個單獨(dú)模塊的名稱(文件夾的名稱)
setConfigFile("mymodule","/WEB-INF/struts-config-mymodule.xml");
?
//
這個地方和前面單模塊的例子有些差別,此處有兩個參數(shù),第一個參數(shù)是模塊的
//
名稱,第二個參數(shù)的意義和上邊相同;
//
和單模塊相比,就上邊兩處區(qū)別
?????? this.setRequestPathInfo("/mymodule","/login.do");
?????? this.addRequestParameter("username","Jplateau");
?????? this.addRequestParameter("password","sandy");
?????? this.actionPerform();
?????? this.verifyForward("success");
?
String expect_username_from_session=”Jplateau”;
?
String actual_username_from_session=this.getSession().getAttribute(“authentication”);
?
this
. assertEquals(“”, expect_username_from_session, actual_username_from_session);
??? }
}
|
?
?
參考資源:
1.
?
http://strutstestcase.sourceforge.net
(本篇文章基本上是沿用該篇文檔的思路,英文好的可以閱讀這篇文檔。)
2.
?
Kent Beck
《
Test-Driven Development By Example
》
3. 本文首發(fā)于本人資料站點(diǎn):http://plateau.sicool.com/
?
Jplateau 2003
年
11
月
12
日星期三
寫于廣州精博