用Apache Cactus 框架工具进行开发和单元测试
![](http://www.ibm.com/i/c.gif)
![]() |
|
软件开发人员,IBM WebSphere Studio Application Developer, IBM 多伦多实验室
2002 年 8 月
通过学习本教程,您将看到 Cactus 是为 EJB、servlet、JSP 和 Filter 等 J2EE 组件提供单元测试支持的另一种方式,它与 Application Developer 的 WebSphere 单元测试环境和服务器工具配合得非常好。
© Copyright International Business Machines Corporation 2002. All rights reserved.
Cactus 是什么?
Cactus
是 Apache Software Foundation 的 Jakarta Project 中的一个开放源代码子工程。它是一个用来对服务器端
Java™ 代码(即 servlet、EJB、Tag 库、Filter 等等)进行单元测试的简单框架;使用 Cactus
的最终目的是为了“降低为服务器端代码编写测试的开销”(请参阅 Apache Cactus 文档中的 Project Description)。Cactus 支持所谓的服务器端代码白箱(white box)测试;它扩展并使用
JUnit(一种回归测试框架)。JUnit 最初是由 Erich Gamma 和 Kent Beck 编写的,您可以在
JUnit单独获取它。JUnit 是 Cactus 分发包附带的,所以您没必要去另外下载它的副本。上面的链接是为那些寻找关于 JUnit 更详细的信息的人而提供的。
Cactus 的 Ant 支持将有助于您更容易实现自动化测试,不过我不会在这里展示这一点。Ant 支持是一个强大的功能,也是 Cactus 和 JUnit 之间的区别所在。Cactus 在最初设计和实现时就决定要支持 集成单元测试,这种测试是 代码逻辑单元测试和 功能单元测试两者之间的折衷方案,在设计 IBM® WebSphere® Studio Application Developer(以下称为 Application Developer)单元测试工具时也部分提供了这种功能。
Cactus 的基本价值主张类似于 Application Developer 中单元测试服务器工具的理念;也就是说,Cactus 允许您实现一种 容器内的 策略(包括“进程内”的概念,而且是在一个 JVM 实例之内),从而在真正的容器中对组件进行单元测试,而在产品中这些组件将被部署到该容器中。我所知道的大多数单元测试环境都使用一种“容器外”的策略, 但是服务器工具和 Cactus 框架都测试在 WebSphere Application Server 本身中运行的组件(也就是说,实际情况并非想象中那般近似)。这只是 Cactus 与单单使用 JUnit 相比所具有的优越性的一个方面。
您需要从应用程序服务器容器内进行测试,才能得到准确的测试结果。您必须这样,因为无论测试环 境如何好,服务器端组件运行的情况都可能与产品模式中(也就是说,在真正的产品级运行时环境中)稍有不同。这可能归咎于多种因素,例如运行时所用的特定 Java JVM、以不可预知的方式进行交互的其它组件、组件上所承受的强度或负载,或者使用不同代码路径的组件逻辑。当您使用 EJB 测试客户机(EJB Test Client)时,您就正在代码运行所在的容器中测试代码。如果发生了任何不希望发生的事,您就会马上看到它。对于 Cactus 也是如此,但要来得简单一点。
教程介绍
下面的教程展示了如何使用 Open Source Apache Software Foundation 的 Cactus 框架(请参阅
Apache Cactus Web 站点)对在中运行的 EJB 中的方法进行单元测试。
通 过学习本教程,您将看到 Cactus 是为 EJB、servlet、JSP 和 Filter 等 J2EE 组件提供单元测试支持的另一种方式,它与 Application Developer 的 WebSphere 单元测试环境和服务器工具配合得非常好。您将一步步学会如何设置该环境。正如所有配置和设置的情况一样,设置 Cactus 框架以使它能在 Application Developer 中工作可能也有其它方法;我只与您分享我使用 Application Developer Version 4.03 进行设置的一种方法。
与我前面撰写的关于在 Application Developer 中进行测试和调试的教程中的风格一样,我还是不会讨论开发 Java、J2EE 应用程序或复杂的 Cactus 单元测试案例的细节问题。相反,本教程的目的只是要表明在 Application Developer 中使用 Cactus 非常容易上手,而且在 Application Developer 中结合了 Cactus 时,您将获得某种“协同作用”(我用“协同作用”这个词是为了表明结合的效果或整体效果要比单独使用两部分加起来的效果更好)。
这个特殊的开放源代码工程可以做到的(Apache Cactus 的参考资料中有所表述),也可以用很多免费软件和开放源代码工具及框架来完成,它们可以用来在 Application Developer 上进行构建。
我不会很详细地讨论 Cactus 框架,因为您可以很容易地在 Apache Cactus Web 站点找到相关的内容。请参阅我以前的一些教程,它们将向您描述在 Application Developer 中创建 J2EE 应用程序并对其进行单元测试的详细情况。第一篇教程 Developing and Testing a Complete "Hello World" J2EE Application with WebSphere Studio Application Developer将向您展示如何创建和测试 EJB。第二篇文章 Developing and Testing a Complete J2EE Application with WebSphere Studio Application Developer ? Part 2:Running on WebSphere Application Server将向您展示如何在 WebSphere Application Server 上部署和测试 J2EE 应用程序。
先决条件
要学习本教程,您需要 WebSphere Studio Application Developer,版本 4.0x(甚至试用版也足够了)和 Cactus 二进制分发包。本教程使用 Cactus 版本 1.3 最终发行版开发和测试。您可以从
Index 页下载二进制文件。
本文本身中没有指定所有的源代码和配置信息。
下面有一些可以下载的文件,其中的
listings.txt
列出了所有源代码、脚本和配置文件的清单。
学习本教程
在本教程中,您将完成下面的任务:
- 下载并解压缩 Cactus 分发包文件
- 编写带有一些简单的业务方法的简单的无状态会话 EJB
- 创建并设置一个 Java 工程以包含要运行的客户机端测试案例
- 创建并设置一个 Web 工程以包含 Cactus“容器内”测试案例
- 从客户机工程复制测试案例到 Web 工程
- 设置 Web 工程的 Java 构建路径
- 设置客户机工程的 Java 构建路径
- 在 WebSphere Application Server 容器中用 Cactus 运行测试案例
一旦所有特定于 J2EE 的资源都完成了,您就会得到一个类似于图 1 的 J2EE View。
下面几个步骤是关于如何开发 EJB、安装 Cactus 框架、编写测试案例以及对将创建的测试案例进行单元测试的。在本教程中,我们将特别集中描述 Cactus 的开发和集成以及一个 Cactus 测试案例。
第 1 步. 下载并解压缩 Cactus 分发包文件
请到 Apache Web 站点
下载 Cactus 1.3 二进制文件的副本到您的开发机器上。在下载了相关文件之后(请注意文件应小于 2Mb),请使用压缩工具将文件解压缩到文件系统上合适的位置。
第 2 步. 编写带有一些简单的业务方法的简单的无状态会话 EJB
在这个步骤中,您将创建一个简单的会话 EJB,它将作为要测试的简单的 EJB 示例。我们将创建并使用一个简单的无状态会话 bean,因为它比较容易理解和开发。
- 请从 Application Developer 的 J2EE 透视图使用 EJB Project Creation 向导在缺省的 EAR 模块中创建一个 EJB 工程。从菜单条选择
File=> New => Project,启动 New Project 向导。从列表中选择 EJB project,并创建一个新的名为
HelloWorldEJB
的 EJB 工程。接受缺省值,单击 Finish。
- 选择
HelloWorldEJB工程(它应该已经在缺省情况下被选中了),然后选择
New => Enterprise Bean创建一个新的会话 EJB。将 EJB 命名为
HelloWorld
,然后按您选择的包名称创建它。在本示例中,我使用了一个名为com.ibm.swosnick.ejb.test
的包。 - 将下面三个业务方法添加到 HelloWorldBean 中,并将它们扩展到远程接口中。您可以随意改变实现情况来满足自己的需求。请参阅
下面的下载文件中的清单 1,那里有代码片段。
public String sayHelloWorld() {
return "Hello World!";
}
public String sayHelloWorld(String firstName) {
return "Hello World " + firstName + "!";
}
public String sayHelloWorld(String firstName, int age) {
return "Hello World " + firstName + ", you are " +
age + " years old!";
}
- 右键单击 EJB 工程,并从弹出菜单选择
Generate deploy and RMIC code。窗口跳出后选中
HelloWorld复选框。确保会话 bean 的 JNDI 名称与您后面查找主接口时将使用的名称一致。
- 使用 EJB Extension Editor 打开 EJB JAR 文件,并修改绑定中的名称。我在示例中使用了一个名为
ejb/HelloWorld
的 JNDI 名称。下面的图 3 展示了您需要指定后面要从测试案例 servlet 使用的 JNDI 名称的地方。 - 一旦您完成了编写和实现您简单的 EJB 的所有步骤,您就应该得到一个类似于图 4 的 EJB 工程。
第 3 步. 创建并设置一个 Java 工程以包含要运行的客户机端测试案例代码
在
本教程中,我们是以一种能让初学者更容易理解,并能够“直观地”看到具体情况的方式来讲述 Cactus 的概念的。一切都包含在
Application Developer 工作区中。这样,我们就能够转移工作区,或与那些其文件系统上可能没有 Cactus
二进制文件的人来共享工作区。我们都承认,这是一个针对简单 EJB 的简单测试案例,不过我还是要说,本教程的重点是在 Cactus 框架本身上。
在 本实现中,您会将各种 Cactus JAR 文件导入到工作区的工程中,然后再将这些(内部的)JAR 文件添加到构建类路径,而不是简单地像添加外部 JAR 文件那样(所添加的路径为解压缩或保存外部 JAR 文件的路径)将它们添加到构建类路径。这会让您很好地了解哪些 JAR 文件需要能够在构建路径和运行时类路径中找到。我还选择了使用一个分开的 Java 客户机工程来在 Web 工程(WAR 模块)中运行测试案例进行测试,而这个 Web 工程本身与 EJB 在同一个应用程序服务器中运行。这就是实际运转中的“容器内”策略 ? WAR 模块与 EJB 模块之间的关系。要创建客户机工程,请完成下面几个步骤。
- 如果您还没有这么做,就请
下载 Cactus 1.3 最终发行版二进制文件的一个副本,并解压缩该文件包。
- 创建一个新的名为
Cactus Client Project
的 Java 工程。
- 要让 Cactus JAR 文件能够在客户机端类路径中找到,请在该工程中创建一个
lib
文件夹,并将 JAR 文件作为 文件(请注意,您希望导入 JAR 文件本身,而不是 JAR 文件中的个别类)从 Cactus 分发包的lib
目录导入到 Application Developer 的工作区中。您应该从 Navigator 透视图进行这一步工作;您需要用将“folder import”指定为“selected types”中的*.jar
,然后指定下面五个 JAR 文件,接下来您要将它们添加到构建路径:
-
aspectjrt.jar
-
cactus.jar
-
commons-httpclient-20020421.jar
-
junit.jar
-
log4j-1.2rc1.jar
- 创建(或从
下面可下载的文件导入)
cactus.properties
文件,作为 Cactus Client Project 的一部分,如下面的图 5 所示。该文件指定 servlet 重定向器的名称(redirector name)和 Web 工程的上下文 URL(context URL):
cactus.contextURL = http://localhost:8080/cactus_test
cactus.servletRedirectorName = ServletRedirector
cactus.enableLogging = true
将
cactus.properties
文件编辑成上面这样就可以导入了,下载文件中就提供了这样一个文件。由于我们在创建和配置 Web 工程以包含服务器端测试案例时将使用cactus_test
这个 Web 上下文根目录,所以我们需要在cactus.properties
文件中将其指定为 contextURL 值。只有在客户机端上才需要这样做。将它导入到 Cactus 客户机工程的根目录中。 - 创建一个新的
log_client.properties
文件(或从 下面可 下载的文件导入该文件)作为 Cactus Client Project 的一部分(请参看图 5),假定您希望使用 Log4j 启用客户机和服务器日志记录支持。您不需要启用日志记录,不过在这里我将说明在您决定使用日志记录时在类路径中需要哪些配置和 JAR 文件。请参阅下载文件中listings.txt
中的清单 3,那里有完整的配置源代码。同样,请将它导入到主工程文件夹中。
- 现在,请创建您的 Cactus 测试案例;它需要物理地存在于客户机和服务器工程中(其原因我们将在后面加以解释)。首先,请在您选择的包中创建一个名为
EJBServletTestCase
的新的 Java 类。在本教程中,我使用了com.ibm.swosnick.test
包。请参阅清单 2,那里有完整的源代码。这个类将继承 Cactus ServletTestCase 并实现setUp()
方法;该方法用来调用标准的样板文件代码从而设置 JNDI 初始上下文,还用来检索对 home 接口的引用。使用您前面绑定到 EJB 的 JNDI 名称来执行查找。当从客户机运行测试案例时,Cactus 框架将为您自动调用查找。
-
EJBServletTestCase
的其它部分是不须说明的,它们包含真正的测试方法。要了解关于如何实现这一点的更多信息,请参阅 Cactus Javadocs 及其文档。在本示例中,您将公开三个测试方法,它们将分别测试 EJB 中臭名昭著的sayHelloWorld()
方法的三个重载变体。在该方法中,您可以选择从 JUnit 框架执行任意数量的断言,以确定给定的条件或给定的状态是否是您期望的。举例来说,下面有一个测试方法,其中有一些简单的断言。
public void testHelloWorld2() throws Exception {
String message = helloWorld.sayHelloWorld("Sheldon");
System.out.println("Message returned: " + message);
assertNotNull(message);
assertEquals(message, "Hello World Sheldon!");
}
- 一旦构建了这个工程(请注意,我们仍然需要设置类路径,这件事我们稍后再做),它应该大致如下面的图 5 所示。
第 4 步. 创建并设置 Web 工程以包含 Cactus“容器内”测试案例
在
该步骤中,您将创建 Web 工程,它将包含“容器内”测试案例以从驻留在同一个 EAR 模块中的 Web 模块对 EJB
上的调用测试方法进行测试。尽管 WAR 模块在 Web 容器中运行,而 EJB 在 EJB
容器中运行,但两个容器都在一个单独的应用程序服务器中运行,该应用程序服务器在一个单独的 JVM
进程中执行。从这个意义上说,应用程序服务器进程本身就是 Web 容器和 EJB 容器的公共容器。
- 在 Application Developer J2EE 透视图中选择
New => Web Project,然后用 Create Web Project 向导在缺省的 EAR 模块中创建一个 Web 工程。将工程命名为
Cactus Testing Web Project
。
- 将 Web 应用程序的上下文根目录设置为
cactus_test
(这是您在cactus.properties
中所使用的 Cactus contextURL 的一部分),然后单击 Finish。
- 使用 Application Developer 的 XML editor 编辑您最初创建 Web 工程时生成的
web.xml
文件。编辑的内容就是添加 servlet 重定向器,Cactus 用它重定向对 EJB 的调用。请参阅 下面的下载文件中的清单 4,那里有完整的web.xml
文件。该配置的重要部分是您把 servlet 和 Web 应用程序的 servlet 映射指定到了哪些地方。
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>org.apache.cactus.server.ServletTestRedirector
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</URL-pattern>
</servlet-mapping>
- 为了简单起见,请从 Cactus 分发包将下面的 JAR 文件作为文件导入到 Web 应用程序的
WEB-INF\lib
文件夹中。
-
aspectjrt.jar
-
cactus.jar
-
junit.jar
-
log4j-1.2rc.jar
- 在
WEB-INF\classes
文件夹中创建或导入一个log_server.properties
文件。这是与您前面添加的log_client.properties
文件等同的服务器端的文件。请参阅下载文件中的清单 5,那里有关于配置的信息。
第 5 步. 把完全一样的测试案例从客户机工程复制到 Web 工程
在 Navigator 视图中,从客户机 Java 工程复制
EJBServletTestCase.java
源文件到 Web 工程的
source
文件夹。缺省情况下,它将在那里被自动编译,相应的类文件将在
WEB-INF\classes
文件夹中被创建,应用程序服务器可以在运行时从该文件夹装载类文件。
您可能觉得很奇怪,为什么我会让您把 完全一样的测 试案例源代码从客户机工程复制到服务器工程。原因在于,这样做支持教程实现 Application Developer 的 Cactus 体系结构的方式。显然,设计 Cactus 框架的方式是使您能够:1)将两个类保持相同的名称,但其实现情况在客户机和服务器上稍有不同,或者 2)在服务器端和客户机端上提供同样的测试案例。该工程的主要贡献者 Vincent Massol 已经向我指出了他们设计 Cactus 时的一些早期的交换情况,他们那时必须作出选择。对于用户编写的每个测试案例,假如用户都必须编写两个类中所有的文本方法,或者只编写一个类中的文本方 法,但必须将该类实例化两次(在服务器端和客户机端各一次,尽管两个端不是在调用相同的方法)。Massol 指出他们选择第二种解决方案是为了减少由于必须创建新测试案例而带来的开销(代价是,要理解其中的机制会复杂一些,不过这毫无疑问提高了易用性)。
因此,正如 Massol 指出的那样,当用户对一个文件进行修改后,他或她还需要对另外一个文件进行同样的修改。或者,您也可以将它们看成是两个有着相同名称的不同文件:客户机端测试案例(它将包含
beginXXX()
和
endXXX()
方法)和服务器端测试案例(它将包含
testXXX()
方法)。也就是说,所有来自 JUnit 的
beginXXX()
和
endXXX()
方法都在客户机端执行,而
testXXX()
方法在服务器端执行。另外,根据 Massol
所说,“并没有硬性要求将同一个类复制两次。可以是两个类(即不同和独有的内容),但名称相同。”我所描述的简单的方法 ? 即在
Application Developer 中实现 Cactus 以在客户机工程和服务器 Web 工程中利用框架(WAR 模块) ? 并不是
Cactus 的最初目的,但是,当我评论 Massol
的观点时,他总结道,“如果它适用于您,那它确实是一种解决方案。……唯一的问题在于,当您编写测试案例时,您必须编写两个类,但
[Application Developer]将使这种工作易如反掌,那么这就根本算不上问题了。而且,[Application
Developer]分开进行两个工程,我同意这样会使事情更简单(特别是对于新手来说)。
在我具体的实现中,
EJBServletTestCase
测试案例类必须存在于两个地方 ? 在客户机和服务器端上 ? 因为它是从两个地方运行的。JUnit testrunner 会在客户机端上运行测试案例的
beginXXXX
和
endXXXX
方法,然后服务器会使用该类的另一个完全一样的副本来在服务器上运行 setup、
testXXXX
和
tearDown
方法。所以,换句话说,要使用我的策略,只需编写一个测试案例类,然后总是将其部署到客户机和服务器就可以了。一旦您理解了这些体系结构的决策,并遵循这个经验之举,为服务器端组件创建有效的测试案例就很简单了。
一旦构建了该工程之后(请注意,我们仍然需要设置这里的类路径),它应该类似于下面的图 6。
第 6 步. 设置 Web 工程的 Java 构建路径
由于代码引用了很多 Cactus 分发包中的类以及 EJB 代码,所以您必须将这些引用添加到构建类路径中。因为您前面将 JAR 文件导入到
WEB-INF\lib
文件夹,并将测试案例导入到源代码文件夹(它构建到
WEB-INF\classes
文件夹),所以 JAR 文件被自动添加到构建路径中。然而,您确实必须解析对 EJB 的引用。
首先,将 HelloWorldEJB工程添加到构建路径,因为您在 servlet 测试案例中引用了 EJB 代码。右键单击工程,然后启动属性(properties)对话框。到 Java Build Path,单击 Projects选项卡。然后选择 HelloWorldEJB工程,如下面的图 7 所示。
我们已经提到过,因为您已经将 JAR 文件添加到
WEB-INF\lib
文件夹,所以它们现在就已经在构建路径上了。请参阅图 8。
- 为客户机工程执行相同的步骤,让构建路径上有 EJB 工程。
- 由于代码引用了很多 Cactus 分发包中的类,所以您必须将这些引用添加到构建类路径中。因为您前面已经将 JAR 文件导入到所创建的
lib
文件夹中,所以将这些 JAR 文件添加到类路径是很简单的一件事。即便您将 JAR 文件导入到工程中,这一步在 Java 工程中还是需要的(这和您将 JAR 文件导入到 Web 工程的WEB-INF\lib
文件夹的情况不一样)。右键单击工程,启动属性对话框。到 Java Build Path,单击 Libraries选项卡。然后,单击 Add JARs按钮,以添加您导入的每个 JAR 文件。如果您需要更多关于这一步的帮助,请参阅 Application Developer 文档。
第 8 步. 在 WebSphere Application Server 容器中用 Cactus 运行测试案例
Eclipse 发行版 2.0 预览版目前支持以集成方式启动 JUnit 测试案例;
Eclipse.org提
供了一些关于为 Eclipse 发行版 1.0 设计的插件的信息,Application Developer 4.03 基于 Eclipse
发行版 1.0。(请注意,如果您愿意,可以运行包含 main() 并调用 JUnit
junit.swingui.TestRunner.main 方法的测试案例类。这就是我在这里描述的方法,它不依赖于为 Application
Developer 4.03 提供的其它插件)。
- 首先,创建一个 WebSphere Application Server 4.0 单元测试服务器工程、服务器实例和服务器配置。向其添加 DefaultEAR。要实现添加,请从菜单条选择
File => New => Project以启动 New Project 向导。从左窗格选择
Server,然后从右窗格选择
Server Project。给服务器工程任意取一个名称。然后单击
Finish。一旦完成,就会切换到 Server 透视图。选择您刚创建的服务器工程,然后右键单击以弹出下拉菜单。选择
New => Server and Server Configuration;Create a New Server and Server Configuration 向导就会被打开。给服务器任意取一个名称,然后从下拉菜单选择
WebSphere v.4.0 Unit Test Environment作为服务器类型。接受其余的缺省选项,然后单击
Finish。
- 以非调试模式启动服务器,因为您不会进行任何调试工作,所以这带来了更高的效率。最容易的方法就是从 Server 透视图的 Server 视图进行;只要单击跑动人像的图标启动服务器就可以了。
- 在 Cactus 客户机工程中,选择 EJBServletTestCase类, 然后单击工具栏上跑动人像的图标。正如前面所述,这将调用 JUnit TestRunnner。一旦 EJB 上的测试被正确执行,TestRunner 的结果应该类似于下面的图 11 所示。我们提供的示例应该不会产生错误,所以您应该不会在结果中看到任何错误。您可能希望故意引入一些错误点,看看会发生什么。
- 控 制台将切换到 JUnit TestRunner 进程,而服务器控制台输出将是不可见的。一旦您定义的三个测试都被执行后,请在进程(process)选项卡中切换回服务器进程,然后看看控制台输出是什 么。它的输出应该表明 ServletRedirector 已经被初始化。从调用三个 EJB 方法返回的字符串也应该被显示出来。
- 如果测试案例成功运行,那么输出到
cactus_client.log
文件的结果(通常写到 Application Developer 的安装根目录)应该类似于下面所示。
|
结束语
本
文描述了一些可以采取的步骤,用来构建一个简单的 J2EE 应用程序,它包括同一个 WebSphere Application Server
服务器容器实例中的 EJB 和 Web 模块。文章还讨论了 Apache Foundation 的 Cactus 框架的集成,这个框架提供了对
J2EE
组件进行单元测试的简单的“容器内”策略。其它开放源代码框架和工具可以以类似的方式进行集成和使用,以扩展(或以某种方式增强)WebSphere
Studio Application Developer 中内置的工具。我希望本教程已经向您展示了对于 Apache Cactus
工程是如何做到这一点的。读者可以利用自己的想像,去发现其它的开放源代码工具和框架能够如何得益于类似的 WebSphere Studio
Application Developer 和开放源代码之间的协同作用。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
- Sheldon Wosnick,
Developing and Testing a Complete "Hello World" J2EE Application with WebSphere
Studio Application Developer,IBM WebSphere 开发者技术期刊,WebSphere 开发者园地,2001 年 10 月。
- Sheldon Wosnick,
Developing and Testing a Complete J2EE Application with WebSphere Studio Application
Developer -- Part 2: Running on WebSphere Application Server,IBM WebSphere 开发者期刊,WebSphere 开发者园地,2001 年 11/12 月。
-
Apache Cactus 主页。
关于作者![]() |
![](http://www.ibm.com/i/c.gif)