StrutsTestCase介绍
StrutsTestCase工程提供了一种在JUnit框架下测试struts action的灵活、便利的方法。你可以通过设置请求参数,检查在Action被调用后的输出请求或Session状态这种方式对Struts Action做白盒测试。StrutsTestCase 提供了用框架模拟web容器的模拟测试方法也提供了真实的web容器(比如:Tomcat)下的测试方法。所有的StrutsTestCase单元测试类都源于模拟测试的 MockStrutsTestCase或容器内测试的CactusStrutsTestCase。
下面的例子就是使用mock测试,因为它需要更少的启动和更快的运行。
要使用StrutsTestCase请在下列地址下载最新的发行包
[url]http://sourceforge.net/project/showfiles.php?group_id=39190[/url]
下面就开始我们的action测试之旅。首先先让我们了解一下要实现的程序的功能:在search.jsp中输入班级编号以列表形式在result.jsp页面中显示相关班级学员信息。在result.jsp中点击学员姓名在info.jsp中显示该学生的详细信息。 下面的请求URL将会显示调用SearchAction类:search.do?method=search
具体步骤如下:
步骤1:在eclipse中放入strutstests-2.1.3.jar,junit.jar、commons-collections.jar
步骤2:编写测试类
构造一个名为SearchActionTest的类并继承MockStrutsTestCase。在该类中添
加测试方法testSearchSuccess
1. import servletunit.struts.MockStrutsTestCase;
2.
3. public class SearchActionTest extends MockStrutsTestCase
4. {
5. public void testSearchSuccess()
6. {
7. setRequestPathInfo("/search.do"); //1行
8. addRequestParameter("method", "search"); //2行
9. actionPerform(); //3行
10. }
11. }
代码解释:
1行:设置请求SearchAction类的请求路径
2行:在请求路径后加上一个请求参数
1行与2行就构成了/search.do?method=search
3行:调用actionPerform()来执行action类
注:以上代码就是对一个测试方法的初始化。可以根据需要自行修改
步骤5:根据struts-config.xml文件完善testSearch方法。先验证可以按照班级名称查询出所需要的数据,主要做以下事情:
a) 初始化提交数据
b) 验证request中"result"属性是否有值
c) 是否返回result.jsp页面
代码如下:
1. public class SearchActionTest extends MockStrutsTestCase
2. {
3. public void testSearchSuccess()
4. {
5. setRequestPathInfo("/search.do");
6. addRequestParameter("method", "search");
7.
8. addRequestParameter("clazzName","gs2t11"); //1行
9. actionPerform();
10.
11. assertNotNull(request.getAttribute("result")); //2行
12. verifyForward("success"); //3行
13. }
14. }
代码解释:
1行:添加提交数据,该数据就相当与从search.jsp提交的数据
2行:查询成功后,将结果放入request中,所以判断result中的"result"属性是否
有值来断定是否将结果成功放入request对象中。
3行:验证查询成功后是否会返回success.jsp页面
运行junit,如果出现以下错误(具体如下图):(请参考附件吧。这里所有的图都省略……)
那么请在src下建立一个名WEB-INF的目录,将WebRoot下的WEB-INF中的web.xml文件与struts-config.xml文件拷贝过去即可。如图:
步骤6:在SearchAction中用最简单的代码先去实现步骤5中的功能
代码如下:
1. public class SearchAction extends DispatchAction
2. {
3. private SearchHandler sh;
4.
5. public ActionForward search(ActionMapping mapping,ActionForm
6. form, HttpServletRequest request, HttpServletResponse response)
7. {
8. SearchFormsearchForm = (SearchForm) form;
9. //获取表单中输入的班级
10. String className = searchForm.getClazzName();
11.
12. //查询该班级的信息
13. sh = new SearchHandler();
14. List list=sh.searchByClassName(className);
15.
16. if(list!=null)
17. {
18. //将班级信息保存至request中,并返回result.jsp
19. request.setAttribute("result",list);
20. return mapping.findForward("success");
21. }
22. else
23. return null;
24. }
25. }
注释比较详细,就不再多解释了
运行测试类结果如下(成功部分编写完毕)
步骤7:编写不能成功取出数据的测试。在SearchActionTest类中添加一个名为testSearchFail的方法
代码如下:
1. public void testSearchFail()
2. {
3. setRequestPathInfo("/search.do");
4. addRequestParameter("method", "search");
5.
6. addRequestParameter("clazzName","gs2txx"); //1行
7. actionPerform();
8.
9. verifyActionMessages(new String[] {"no.stuclass.found"});//2行
10. verifyForward("error"); //3行
11.
12. verifyForwardPath("/error.jsp"); //4行
13. }
代码解释:
1行:输入一个不存在的班级
2行:验证向ActionMessages中放入的消息文本
3行:验证返回地址
4行:验证页面地址是否与转向的error对应
步骤8:更改SearchAction类,实现步骤7的功能
代码如下:(粗体为增加部分)
1. public class SearchAction extends DispatchAction
2. {
3. private SearchHandler sh;
4.
5. public ActionForward search(ActionMapping mapping,ActionForm
6. form, HttpServletRequest request, HttpServletResponse response)
7. {
8. SearchFormsearchForm = (SearchForm) form;
9. //获取表单中输入的班级
10. String className = searchForm.getClazzName();
11.
12. //查询该班级的信息
13. sh = new SearchHandler();
14. List list=sh.searchByClassName(className);
15.
16. if(list!=null)
17. {
18. //将班级信息保存至request中,并返回result.jsp
19. request.setAttribute("result",list);
20. return mapping.findForward("success");
21. }
22. else
23. {
24. //构造一个ActionMessage保存一条消息
25. ActionMessages messages=new ActionMessages();
26. messages.add(Globals.MESSAGE_KEY, new ActionMess("no.stuclass.found"));
saveMessages(request,messages);
27. //返回error.jsp
28. return mapping.findForward("error");
29. }
30. }
31. }
运行测试类,正确
经过前面两个功能的测试与运行,两个功能都可以单独测试通过,由于第二个功能是以第一个功能成功查询为前提才能运行,所以再编写一个测试,将两个 action功能整合。这样就可以测试整个功能模块是否正确。在DisplayActioTest中编写一个名为 testSearchAndDisplay()的方法来完成这个测试吧。
代码如下:
1. public void testSearchAndDisplay()
2. {
3. //先调用SearchAction来取得数据
4. setRequestPathInfo("/search.do");
5. addRequestParameter("method", "search");
6.
7. addRequestParameter("clazzName","gs2t11");
8. actionPerform();
9.
10. List list=(List) request.getAttribute("result");
11. Student student=(Student) list.get(0);
12.
13. //调用DisplayAction类显示在result.jsp中选择的数据
14. setRequestPathInfo("/display.do");
15. addRequestParameter("method","display");
16. addRequestParameter("stuName", student.getStuName());
17.
18. actionPerform();
19.
20. assertNotNull(request.getAttribute("stu"));
21. verifyForward("info");
22. }
运行测试类,测试成功
整个模块到这里全部开发测试完毕,但是还没完,作为测试驱动开发最重要的一环就是重构,这样才能保证你程序的健壮性、可读性、可维护性及优雅性。这里重构主要是针对Action类进行重构,你也可以用同样的方法在编写完测试代码之后对测试代码进行重构。这里主要针对以下几个地方进行重构
1) 抽取Action类中的变量:
SearchAction类中:
1. SearchForm searchForm= (SearchForm) form;
2. String className = searchForm.getClazzName();
3. sh = new SearchHandler();
4. List list=sh.searchByClassName(className);
可以用下列代码替换:
1. sh = new SearchHandler();
2. List list=sh.searchByClassName(((SearchForm) form).getClazzName());
DisplayAction类中:
1. DisplayForm displayForm = (DisplayForm) form;
2. String name=displayForm.getStuName();
3. Student stu=sh.displayByName(name);
4. request.setAttribute("stu",stu);
可以用下列代码替换:
1. request.setAttribute("stu",sh.displayByName(((DisplayForm)form).getStuName()));
2) 抽取常量:
SearchAction类中:
1. request.setAttribute("result",list);
可以用下列代码替换
1. private static final String RESULT = "result";
2. request.setAttribute(RESULT,list);
SearchAction类中
1. request.setAttribute("stu",sh.displayByName(((DisplayForm) form).getStuName()));
可以用下列代码替换
1. private static final String STUDENT = "stu";
2. request.setAttribute(STUDENT,sh.displayByName(((DisplayForm) form).getStuName()));
构造一个名为BaseAction的类,继承DispatchAction类,并在其中初始化一个SearchHandler对象
1. public class BaseAction extends DispatchAction
2. {
3. protected SearchHandler sh=new SearchHandler();
4. }
SearchAction类与DisplayAction类可改为
1. public class SearchAction extends BaseAction
2. public class DisplayAction extends BaseAction
同时要去掉两个类中的SearchHandler属性
SearchAction类中下列代码可以抽取成方法:
1. ActionMessages messages=new ActionMessages();
2. messages.add(Globals.MESSAGE_KEY, new ActionMessage("no.stuclass.found"));
3. saveMessages(request,messages);
可以用下列代码替换:
1. addResultMessage(request,"no.stuclass.found");
2.
3. private void addResultMessage(HttpServletRequest request,String message)
4. {
5. ActionMessages messages=new ActionMessages();
6. messages.add(Globals.MESSAGE_KEY, new ActionMessage("no.stuclass.found"));
7. saveMessages(request,messages);
8. }
由于在别的Action中也要经常生成ActionMessages对象,所以再将该方法 抽取到BaseAction中。
最后BaseAction类代码如下:
1. public class BaseAction extends DispatchAction
2. {
3. protected SearchHandler sh=new SearchHandler();
4.
5. protected void addResultMessage(HttpServletRequest request, String message)
6. {
7. ActionMessages messages=new ActionMessages();
8. messages.add(Globals.MESSAGE_KEY, new ActionMessage(message));
9. saveMessages(request,messages);
10. }
11. }
最后代码效果请参考源代码。
4) 重构注意事项:
a) 每次要一点点的重构,万万不要一次重构很多地方。
b) 每重构完一个地方要运行测试类来保证其正确性。
c) 不要怕麻烦。在后期重构的优势就会越来越明显。
到此为止,全部的测试与代码编写已经结束。还等什么,将已经做好的页面套上去吧,感受一下不用启动服务器来开发web应用程序的乐趣吧,感受测试驱动开发给你带来的开发思想的质的飞跃……
·
·
·
·
· 在Eclipse中使用StrutsTest进行Struts的单元测试[转载]
下面在测试方法中就可以填写自己需要的东西了。如下面的例子:
package com.ezcross.SMS.common.struts.action;
import servletunit.struts.MockStrutsTestCase;
/**//**
* @author paul.ping(wp.huang@ezcross.com)
*
* 类名:SMS10Test
* 操作:
*/
public class LoginActionTest extends MockStrutsTestCase {
/**//*
* @see TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
}
/**//*
* @see TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
public void testDoExecute() {
try
{
addRequestParameter("user_name","cka");
addRequestParameter("passward","111111");
setRequestPathInfo("/login");
System.out.println("hava completed adding data");
actionPerform();
System.out.println("begin verify");
verifyForward("dologin");
verifyForwardPath("/Login.jsp");
verifyNoActionErrors();
System.out.println("end verify");
//assertNotNull(testObject.doExecute(null,null,null,null,null));
}
catch (Exception ee)
{
System.out.println(ee.toString());
}
}
}
填写完程序后,右击程序,选择run下的JUnit Test进行运行,如果没有错误的话则显示绿色 ,否则显示红色。
对程序的说明:
主要函数和作用:
- setContextDirectory,设置web应用的根
- setRequestPathInfo,设置request的请求
- addRequestParameter,将参数和对应的值加入request中
- actionPerform,执行这个请求
- verifyForward,验证forward的名字是否正确
- verifyForwardPath,验证forward的path是否正确
- verifyNoActionErrors,验证在action执行过程中没有ActionError产生
- verifyActionErrors,验证在action执行过程中产生的ActionError集合的内容
具体详细的内容请参见对应的javaDoc。