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