關鍵字: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的使用不做介紹,請參考相關資料
<forward name="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
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/