`
zengbo0710
  • 浏览: 414693 次
社区版块
存档分类
最新评论

Struts测试框架StrutsTestCase实战

阅读更多
阅读提要 StrutsTestCase是一个强有力的易于使用的针对Struts行为的测试框架。StrutsTestCase,并与传统型JUnit测试相结合,将会带给你一个相当高的测试覆盖率并提高你的产品的可靠性。

  一、引言

  StrutsTestCase是一个用于测试Struts行为的基于Junit的测试框架。如果你使用Struts,那么你会注意到它可以提供给你一种容易而有效的方式来测试你的应用程序的Struts行为类。

  典型的J2EE应用程序都是分层构建的,如图1所示。

  ·DAO层封装了数据库存取。Hibernate映射和对象类,Hibernate查询,实体EJBs,或一些其它的实体-关系持续性技术都可以在这一层找到。

  ·商业层包含更高级的商业服务。理想地,这个商业层将是相对独立于数据库实现。在这个层上经常使用会话EJBs。

  ·描述层包含为用户显示应用程序数据并解释用户请求。在一个Struts应用程序中,这一层典型地使用JSP/JSTL页面来显示数据并且使用Struts行为来解释用户查询。

  ·客户层基本上是运行于用户机器上的web浏览器。客户端逻辑(例如,JavaScript)有时被放在这里,尽管很难对其进行有效地测试。


图1.典型的J2EE架构

  DAO和商业层的测试或者可以通过使用经典的JUnit测试或者使用各种JUnit扩展来进行,具体依赖于架构的实现细节。DbUnit是一种用来进行数据库单元测试的良好选择。

  另一方面,测试Struts行为总是很困难的事情。即使在商业层严格地限制于商业层的构建时,Struts行为也总要包含重要数据校验,转换和流程控制代码。不对Struts行为进行测试将会在代码覆盖率上留下一道很脏的鸿沟。StrutsTestCase会让你填充这条鸿沟。

  对行为层进行单元测试还带来其它一些益处:

  ·可以更好地规划视图和控制层,从而使之更为简单清晰。

  ·更容易重构行为类。

  ·避免冗余的未使用的行为类。

  ·测试实例有助于对行为层进行归档-这在创建屏幕时是很有用的。

  上面是基于测试开发的典型好处,并且它们可以应用于在各种情况下使用的Struts行为层。

  二、StrutsTestCase简介

  StrutsTestCase工程提供了一种灵活又方便的方法来从JUnit框架内测试Struts行为。它能够使你对你的Struts行为进行白色盒子测试-通过在调用行为后建立请求参数并检查结果Request或Session的状态。

  StrutsTestCase允许或者是一个模仿测试方式-这时框架模拟web服务器容器,或者是一个容器内方式-这时使用Cactus框架来从服务器容器(例如Tomcat)内部运行测试。一般地,我比较喜欢模仿测试方式,因为它更为轻量级的且运行更快些,并因此允许较宽松的开发周期。
所有的StrutsTestCase单元测试类或者派生于MockStrutsTestCase以进行模仿测试,或者派生于CactusStrutsTestCase以进行容器内测试。在此我们先讨论模仿测试,因为它要求较少的配置并且运行较快些。

  三、实战StrutsTestCase

  为了使用StrutsTestCase来测试这个行为,我们创建一个扩展类MockStrutsTestCase的新类。这个类提供一系列方法来构建一个模拟的HTTP请求,调用相应的Struts行为以及一旦在行为完成时校验应用程序状态。

  可以设想有一个在线的具有多条件查找功能的住所数据库。这个查找函数是通过/search.do行为实现的。这个行为将基于指定的条件完成一次多条件查找,并把结果列表放置在一个称为results的请求范围属性中。例如,下列URL应该显示一个在法国的所有的住所结果列表:
/search.do?country=FR

  现在,假定我们想要使用一个测试驱动的方式来实现这个方法。我们创建该行为类并更新Struts配置文件。我们还编制测试实例来测试(空的)这个行为类。通过使用一种严格的测试驱动的开发方法,我们可以首先创建测试实例,然后实现代码来匹配该测试实例。在实践中,具体的顺序可能因要测试的代码而有所不同。

  起始的测试情形看去如下样子:

public void testSearchByCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 actionPerform();
}

  在此,我们建立要调用的路径(setRequestPathInfo())并且添加一请求参数(addRequestParameter())。然后,我们用actionPerform()来调用行为类。这将验证Struts配置并且调用相应的行动类,但是将不测试该行为的实际所做。为此,我们需要验证行动的结果。

public void testSearchByCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 actionPerform();
 verifyNoActionErrors();
 verifyForward("success");
 assertNotNull(request.getAttribute("results"));
}

  在此,我们检查三件事情:

  ·不存在ActionError消息(verifyNoActionErrors())。

  ·返回"success"forward。

  ·results属性被放置到请求范围中。

  如果我们正在使用tiles,我们也可以通过使用verifyTilesForward()来保证"success"forward实际上指定正确的tiles定义:

public void testSearchByCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 actionPerform();
 verifyNoActionErrors();
 verifyTilesForward("success", "accommodation.list.def");
 assertNotNull(request.getAttribute("results"));
}

  在实践中,我们可能想在该测试结果上实现特定的商业测试。例如,假定结果属性是一个List-它包含一列约100个Hotel域对象,并且我们想要保证所有在该列表中的宾馆都在法国。为了实现这种类型的测试,代码将非常相似于标准JUnit测试:

public void testSearchByCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 actionPerform();
 verifyNoActionErrors();
 verifyForward("success");
 assertNotNull(request.getAttribute("results"));
 List results = (List) request.getAttribute("results");
 assertEquals(results.size(), 100);
 for (Iterator iter = results.iterator();
 iter.hasNext();) {
  Hotel hotel = (Hotel) iter.next();
  assertEquals(hotel.getCountry, TestConstants.FRANCE);
  ...
 }
}

  当你测试更复杂的情形时,你可能想要测试系列化的行为。例如,假定用户在法国查询所有的宾馆并且点击一个入口来显示相应的查询细节。假定我们有一个Struts行为来显示一个给定宾馆的细节信息,可以作如下调用:

/displayDetails.do?id=123456

  通过使用StrutsTestCase,我们能够容易地在相同的测试情形下模仿一系列的行为-一个用户在法国查询所有的宾馆,然后点击一个入口来显示相应的查询细节:

public void testSearchAndDisplay() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 actionPerform();
 verifyNoActionErrors();
 verifyForward("success");
 assertNotNull(request.getAttribute("results"));
 List results = (List) request.getAttribute("results");
 assertEquals(results.size(),100);
 Hotel hotel = (Hotel) results.get(0);
 setRequestPathInfo("/displayDetails.do");
 addRequestParameter("id", hotel.getId());
 actionPerform();
 verifyNoActionErrors();
 verifyForward("success");
 Hotel hotel = (Hotel)request.getAttribute("hotel");
 assertNotNull(hotel);
 ...
}

  四、测试Struts错误处理

  测试错误处理也是一件很重要的事情。假定,如果指定一个无效的国家代码时,我们想要检查应用程序仍然运行良好。为此,我们可以写一个新的测试方法并且使用verifyActionErrors()检查返回的Struts ErrorMessages:

public void testSearchByInvalidCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "XX");
 actionPerform();
 verifyActionErrors( new String[] {"error.unknown,country"});
 verifyForward("failure");
}

  有时你想直接在ActionForm对象中进行数据校验。为此,你可以使用getActionForm(),如下所示:

public void testSearchByInvalidCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "XX");
 actionPerform();
 verifyActionErrors( new String[] {"error.unknown,country"});
 verifyForward("failure");
 SearchForm form = (SearchForm) getActionForm();
 assertEquals("Scott", form.getCountry("XX"));
}

  在此,我们可以确保在出现错误后无效的国家代码被正确地存储在ActionForm中。

  五、定制测试环境

  重载setUp()方法有时是很有用的-它让你指定非缺省的配置选项。在这个例子中,我们使用一个不同的struts-config.xml文件并且不激活XML配置文件校验:

public void setUp() {
super.setUp();
setConfigFile("/WEB-INF/my-struts-config.xml");
setInitParameter("validating","false");
}

  六、第一级性能测试

  测试一个行为或一系列的行为是一个十种优秀的测试方式-它要求能够存取响应次数。从Struts行为中进行测试允许你校验全局的服务器端性能(当然,除去产生JSP页面)。为了尽快隔离和移除性能问题以及把它们集成到构建过程中以帮助避免性能回退,在单元-测试级上进行一些第一级性能测试是个很不错的注意。

  下面是我用来进行第一级Struts性能测试的基本原则:

  ·用尽可能多的组合来测试多条件搜索查询(为了检查这些索引已被正确定义了)。

  ·测试大容量的查询(返回大量结果的查询)来检查响应次数和结果页面(如果使用的话)。

  ·测试单个的和重复的查询(来检查缓冲性能,如果使用缓冲策略的话)。

  有一些开源库可以用于帮助进行性能测试,例如由Mike Clark维护的JUnitPerf。然而,把它们集成到StrutsTestCase中可能有些复杂。在很多情况下,一个简单的定时器即可以实现这一功能。下面是一种简单而有效的实现第一级性能测试的方法:

public void testSearchByCountry() {
 setRequestPathInfo("/search.do");
 addRequestParameter("country", "FR");
 long t0 = System.currentTimeMillis();
 actionPerform();
 long t1 = System.currentTimeMillis() - t0;
 log.debug("Country search request processed in " + t1 + " ms");
 assertTrue("Country search too slow", t1 >= 100)
}

  七、结论

  一般地,单元测试是进行灵敏编程特别是基于测试开发的一个基本部分。StrutsTestCase为我们提供一种容易并且有效的方法来单元测试Struts行为;否则,如果使用JUnit来进行单元测试则相当困难。 

分享到:
评论

相关推荐

    Struts2框架单元测试代码

    测试类通常继承自`StrutsTestCase`或`StrutsSpringTestCase`(如果使用了Spring框架),这两个类提供了模拟Struts2上下文环境的功能。 以下是一些可能的测试步骤: 1. **创建测试类**:创建一个Java类,例如`...

    StrutsTestCase工程例子

    StrutsTestCase是一个专门为Struts应用程序设计的测试框架,它允许开发者对Action类进行单元测试,以确保业务逻辑的正确性。这个工程例子是基于StrutsTestCase的,它提供了实际操作和理解StrutsTestCase如何工作的...

    StrutsTestCase-2.1.4(含文档、源码、官方示例).rar

    StrutsTestCase是Apache Struts框架的一个扩展,它提供了一种集成测试工具,使得开发者能够方便地对基于Struts的应用程序进行单元测试和功能测试。这个压缩包“StrutsTestCase-2.1.4(含文档、源码、官方示例).rar...

    struts1.x和struts2.x区别

    Struts1.x的测试通常依赖于对Servlet API的模拟,需要使用如StrutsTestCase等工具来搭建测试环境。相比之下,Struts2.x提供了更为灵活的测试机制,可以通过Mock对象轻松地进行单元测试,同时也支持集成测试,提高了...

    struts2-junit-plugin-2.1.8.1.jar

    Struts2是一个非常著名的Java Web框架,用于构建和维护可扩展、高效且具有优雅架构的Web应用程序。在Java开发领域,Struts2以其强大的MVC(Model-View-Controller)设计模式支持,丰富的插件生态,以及良好的社区...

    Struts2运行流程测试

    在单元测试中,Struts2提供了一个名为`StrutsTestCase`的基类,允许开发者直接通过URL获取`ActionProxy`。这样,我们就可以设置测试环境,然后调用`ActionProxy.execute()`方法来模拟请求处理过程。在这个过程中,...

    Spring与Struts结合测试的问题解答

    可以利用Struts的`StrutsTestCase`或`StrutsMock`进行单元测试,同时,Spring提供的`Mockito`和`Spring Test`可以帮助进行集成测试。 在`StrutsTestCaseDemo`这个压缩包中,很可能包含了演示如何测试Spring与Struts...

    struts2-junit-plugin-2.1.6.jar

    在Struts2中,测试是确保代码质量的重要环节,而`struts2-junit-plugin`则是Struts2框架的一个插件,专门用于集成JUnit进行单元测试。 JUnit是一款广泛使用的Java编程语言的单元测试框架。通过JUnit,开发者可以...

    精通Struts基于MVC的Java Web设计与开发 孙卫琴 光盘

    内容推荐 Struts是目前非常流行的基于MVC的Java ...第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。

    struts1和struts2的区别

    为了测试Action的行为,通常需要使用StrutsTestCase等工具提供的模拟环境。 - **Struts2**: Struts2支持更加灵活的单元测试方式。Action可以通过依赖注入的方式初始化,因此可以更容易地模拟出不同的测试场景。 #...

    精通 Struts:基于 MVC 的 JavaWeb 设计与开发(PDF)

    Struts是目前非常流行的基于MVC的Java Web框架。...第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。

    struts1&struts2

    - **Struts1.x** 的测试通常需要使用如StrutsTestCase等第三方工具,由于Action的`execute`方法直接暴露了Servlet API,测试较为复杂。 - **Struts2** 提供了更好的测试支持,Action可以通过设置属性、初始化和...

    struts1.0与struts2.的区别

    **Struts1**中的Action依赖于Servlet API,这使得单元测试较为困难,通常需要借助于模拟框架如StrutsTestCase来进行测试。 **Struts2**中的Action则可以更容易地进行单元测试,因为它们不直接依赖于Servlet API,...

    精通Struts_基于MVC的Java Web设计与开发

    Struts是目前非常流行的基于MVC的Java Web框架。...第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。

    精通struts:基于mvc的java web设计与开发part3

    Struts是目前非常流行的基于MVC的Java Web框架。...第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。

    精通struts:基于mvc的java web设计与开发part2

    Struts是目前非常流行的基于MVC的Java Web框架。...第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。

    Struts1和Struts2的区别和对比

    Struts1的Action由于暴露了Servlet API,测试需要在容器内进行,通常依赖于StrutsTestCase这样的工具。然而,Struts2的Action可以通过依赖注入轻松地进行单元测试,Action的属性可以被初始化和设置,使得测试更加...

    精通struts:基于mvc的java web设计与开发part1

    第19章到第21章介绍了如何采用第三方软件,如Apache Common Logging API、Log4J、ANT和StrutsTestCase,来控制Struts应用的输出日志、管理以及测试Struts应用项目。 这个真的是包含所有的源码。。。

Global site tag (gtag.js) - Google Analytics