[Struts]使用StrutsTestCase对Action进行单元测试简介

简介: 目前,测试驱动开发正变得越来越流行,由于“存在的就是合理的”,这种开发方式必然有其优越之处。作为一个小小程序员,对新鲜技术的追求是工作的重要动力,相信大家都有同感吧。 测试驱动开发是极限编程(XP)的重要组成部分,从字面上就可以看出,它是先有测试再有代码的。

目前,测试驱动开发正变得越来越流行,由于“存在的就是合理的”,这种开发方式必然有其优越之处。作为一个小小程序员,对新鲜技术的追求是工作的重要动力,相信大家都有同感吧。

测试驱动开发是极限编程(XP)的重要组成部分,从字面上就可以看出,它是先有测试再有代码的。这听起来似乎有点奇怪,实际上,可以把测试用例当作需求,程序员的工作就是写出满足这种需求的代码,即让这些测试都能够通过。在刚刚写好测试用例的时候,由于还没有实际代码,因此这时运行测试的结果一定不会通过,随着代码的增加,越来越多的测试得以通过,最后全部通过。这时,基本上可以说系统的每个功能单元都是正确的,剩下的工作就是集成测试了。而这些测试用例的使命并未结束,因为代码会不断修改,我们需要经常(例如每天)运行测试来检验目前的代码是否能够通过测试。很明显,这种检验是完全自动化的,既快速有保证质量。

在使用Struts构件的系统里,的每一个Action都可以看作一个功能单元,它们构成了系统的主体。(当然,你的业务逻辑并不一定都直接写在Action的execute方法中,但在该方法中会以一定方式调用这些逻辑。)StrutsTestCase(http://strutstestcase.sourceforge.net/)是JUnit的一个包装,它提供了非常方便的测试这些Action的方法。它提供两种测试方式:Mock(模拟对象)和Cactus(真实环境),目前我只试验了前者,发现效果很好。

由于我已经写了一些代码,因此我进行的不能算是真正的测试“驱动”开发,我主要是把StrutsTestCase作为一种自动化的测试工具来用,起到保证代码质量的作用。

举例来说,我有一个类名为SaveTeacherAction的Action,其所在模块名为teacher,访问路径为/save,与他相关联的是名为TeacherForm的ActionForm,(struts-config-teacher.xml)配置如下所示:

None.gif < action
None.gif    attribute
= " teacherForm "
None.gif    input
= " /form/teacher.jsp "
None.gif    name
= " teacherForm "
None.gif    path
= " /save "
None.gif    scope
= " request "
None.gif    type
= " edu.pku.cc.democenter.teacher.action.SaveTeacherAction " >
None.gif    
< forward name = " success "  path = " /list.do "  redirect = " true "   />
None.gif
</ action >
None.gif

SaveTeacherAction类中的execute方法如下,其中HibernateDAO是我自己写的用来进行持久化操作的包装类,BeauUtils是Jakarta commons包中的一个实用工具,可以在两个Bean类型对象的相同属性之间进行复制:

None.gif public ActionForward execute(
None.gif    ActionMapping mapping,
None.gif    ActionForm form,
None.gif    HttpServletRequest request,
None.gif    HttpServletResponse response)
None.gif    throws Exception {
None.gif
None.gif    HibernateDAO dao
= HibernateDAO.getInstance(getServlet().getServletContext());
None.gif    TeacherForm teacherForm 
=  (TeacherForm) form;
None.gif    Teacher t
= null ;
None.gif    
None.gif    
if ( " Create " .equals(teacherForm.getAction())){
None.gif        t
= new  Teacher();
None.gif    }
None.gif    
if ( " Edit " .equals(teacherForm.getAction())){
None.gif        t
= (Teacher)dao.findByCode(Teacher.class,teacherForm.getCode());
None.gif    }
None.gif    
None.gif    BeanUtils.copyProperties(t,teacherForm);
None.gif    
None.gif    dao.saveOrUpdate(t);
None.gif    
return  mapping.findForward( " success " );
None.gif}
None.gif

下面,我要写一个测试用例来对这个Action进行测试。在Eclipse里使用StrutsTestCase非常简单,只需要在工程的classpath里包含strutstest-2.1.2.jar这个包以及junit的包就可以开始编写了。我为这个类起名为TestSaveTeacherAction,即Action类名前加上Test字样,所在包也与实际代码分开,使用edu.pku.cc.democenter.test的名称(与之对比,SaveTeacherAction的包名为edu.pku.cc.democenter.teacher.action,democenter是我们这个项目的名称)。

现在来看一下TestSaveTeacherAction的内容:

None.gif package edu.pku.cc.democenter.test;
None.gif
None.gifimport servletunit.struts.MockStrutsTestCase;
None.gifimport edu.pku.cc.democenter.teacher.form.TeacherForm;
None.gif
None.gifpublic class TestTeacherAction extends MockStrutsTestCase{
None.gif
None.gif    protected 
void  setUp() throws Exception {
None.gif        super.setUp();
None.gif        setConfigFile(
" teacher " , " /WEB-INF/struts-config-teacher.xml " );
None.gif    }
None.gif
None.gif    protected 
void  tearDown() throws Exception {
None.gif        super.tearDown();
None.gif    }
None.gif
None.gif    public 
void  testSaveTeacherAction_Create(){
None.gif        setRequestPathInfo(
" /teacher " , " /save " );
None.gif        TeacherForm form
= makeForm();
None.gif        form.setAction(
" Create " );
None.gif        form.setCode(
" test.create " );
None.gif        setActionForm(form);
None.gif        actionPerform();
None.gif        verifyForward(
" success " );
None.gif    }
None.gif    
None.gif    public 
void  testSaveTeacherAction_Edit(){
None.gif        setRequestPathInfo(
" /teacher " , " /save " );
None.gif        TeacherForm form
= makeForm();
None.gif        form.setAction(
" Create " );
None.gif        form.setCode(
" test.edit " );
None.gif        setActionForm(form);
None.gif        actionPerform();
None.gif        
None.gif        form.setAction(
" Edit " );
None.gif        form.setBirthDate(
" 1979-1-10 " );
None.gif        setActionForm(form);
None.gif        actionPerform();
None.gif        verifyForward(
" success " );
None.gif    }
None.gif
None.gif    private TeacherForm makeForm(){
None.gif        TeacherForm form
= new  TeacherForm();
None.gif        form.setAction(
" Create " );
None.gif        form.setBirthDate(
" 1979-1-1 " );
None.gif        form.setCode(
" test.001 " );
None.gif        form.setEmail(
" test@test.com " );
None.gif        form.setName(
" test " );
None.gif        form.setShortName(
" CS " );
None.gif        form.setTel(
" 62760000 " );
None.gif        
return  form;
None.gif    }
None.gif}
None.gif

使用Mock方式的测试用例都继承servletunit.struts.MockStrutsTestCase这个类,setUp和tearDown方法分别是测试前后进行准备和善后工作的地方。要运行的测试方法都以test开头,系统会自动调用这些方法。

由于SaveTeacherAction根据request中的action参数有两种运行方式:Create和Edit,所以我写了两个test方法对它们分别进行测试。这里要注意的是,这些test方法运行的顺序是不确定的,不要认为可以先运行create的测试,再以此为基础对刚刚create的对象进行edit,看testSaveTeacherAction_Edit方法的写法。

我在setUp方法里指定了模块和对应的配置文件名称,如果没有模块可以省去这一步。在实际的testSaveTeacherAction_Create方法里,首先用setRequestPathInfo方法指定要测试的Action的路径和模块,如果没有模块可以用一个参数的同名方法。然后构造一个ActionForm,对于我的例子就是TeacherForm,为了简化代码,我写了一个makeForm方法来生成一个填好值的TeacherForm。用setActionForm将这个TeacherForm连接到Action,actionPerform方法通知执行Action操作。这时按照我们的设想,Action会将请求转发到一个名为success的Forward,所以我们使用verifyForward("success")方法验证是否进行了转发。在这一步如果失败,就表明此单元测试失败,否则为成功。

testSaveTeacherAction_Edit方法与其类似,只是要注意在这个方法里要自己建立对象再修改,而不能使用testSaveTeacherAction_Create方法里建立的对象,因为这两个方法的执行顺序是不确定的。

要在Eclipse里运行这个测试也很简单,先双击打开这个类,然后在Run菜单里选择Run As->JUnit Test就可以了,你会看到一个JUnit视图,里面有一个绿色的进度条,如果走到头还保持绿色表示所有的测试都成功,否则会变成红色,并在下面显示异常的堆栈信息。(成就感哦)

junit.gif

好了,今天对StrutsTestCase作了一个很简单的介绍,我也是刚刚开始使用它,今后肯定还会遇到问题的,敬请关注后续报道。

另,相关的一些文章可以在网上找到,作为入门很好,例如:

http://plateau.sicool.com/article/tdd/strutstestcast_junit_tdd.htm

我的感觉,遇到问题最好先找文档,养成习惯后不但解决问题快了,同时在看文档的同时还可以对其加深理解,何乐而不为呢。

本文转自博客园八进制的博客,原文链接:[Struts]使用StrutsTestCase对Action进行单元测试简介,如需转载请自行联系原博主。

相关文章
|
5月前
|
XML 测试技术 数据格式
Python性能测试Locust简介
Python性能测试Locust简介
Python性能测试Locust简介
|
5月前
|
JSON 测试技术 API
『Postman入门万字长文』| 从工具简介、环境部署、脚本应用、Collections使用到接口自动化测试详细过程
『Postman入门万字长文』| 从工具简介、环境部署、脚本应用、Collections使用到接口自动化测试详细过程
80 3
|
5月前
|
XML Java 测试技术
『App自动化测试之Appium应用篇』| 元素定位工具Appium-Inspector从简介、安装、配置到使用的完整攻略
『App自动化测试之Appium应用篇』| 元素定位工具Appium-Inspector从简介、安装、配置到使用的完整攻略
201 3
|
5月前
|
XML 测试技术 开发工具
『App自动化测试之Appium应用篇』| 元素定位工具uiautomatorviewer从简介、特点、启动到使用的完整过程
『App自动化测试之Appium应用篇』| 元素定位工具uiautomatorviewer从简介、特点、启动到使用的完整过程
93 4
|
4月前
|
SQL Java 数据库连接
Mybatis之Mybatis简介、搭建Mybatis相关步骤(开发环境、maven、核心配置文件、mapper接口、映射文件、junit测试、log4j日志)
【1月更文挑战第2天】 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
208 3
Mybatis之Mybatis简介、搭建Mybatis相关步骤(开发环境、maven、核心配置文件、mapper接口、映射文件、junit测试、log4j日志)
|
4月前
|
安全 测试技术 数据库
简介性能测试
简介性能测试
简介性能测试
|
4月前
|
测试技术
HTTP性能测试工具Siege 简介
HTTP性能测试工具Siege 简介
|
5月前
|
Linux 网络安全 开发工具
Android APP入门之Android的简介以及Android Studio开发环境的搭建和测试(2022最新 图文解释 简单易懂)
Android APP入门之Android的简介以及Android Studio开发环境的搭建和测试(2022最新 图文解释 简单易懂)
67 0
|
5月前
|
Java 测试技术 持续交付
百度搜索:蓝易云【NetMock简介:简化 Java,Android和Kotlin多平台中的HTTP请求测试?】
使用NetMock,您可以在单元测试、集成测试和端到端测试中轻松地模拟和验证HTTP请求和响应,而无需实际发送请求到外部服务。这样可以提高测试的可靠性和可控性,并加快测试执行的速度。无论是在开发过程中还是在持续集成环境中,NetMock都可以帮助您更轻松地进行HTTP请求测试,提高代码质量和稳定性。
41 1
|
12月前
|
Java 测试技术 程序员
「敏捷架构」核心实践:测试驱动开发(TDD)简介
「敏捷架构」核心实践:测试驱动开发(TDD)简介