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

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

测试驱动开发是极限编程(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

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

转载于:https://www.cnblogs.com/bjzhanghao/archive/2004/07/29/28221.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值