UML软件工程组织

 

 

对 Java 和 EJB 应用程序进行单元测试
 
作者: Abraham WoldeMichael 来源: 网络转载
 

此篇文章是以 JUnit 和 JunitEE 的概述开始的,然后展示了 JUnit 和 JUnitEE 在 Rational Application Developer IDE 中的用法,即如何对一个简单的 Java 应用软件和一个无状态的 session bean 进行单元测试。首先,我们介绍了 JUnit 和 JUnitEE 结构的简单概述,紧接着我们介绍了如何在 JUnit 和 JUnitEE 测试框架中开发,配置以及安装单元测试。最后,我们阐述了如何在应用软件服务器环境中布署和执行单元测试。

JUnit 测试框架

JUnit 现在是基于 Java 应用软件单元测试的非官方标准。尽管 Junit.org Web 网站提供了更多有压缩力的信息和指南(请参见 参考资源),这个部分还是向您提供了 JUnit 测试框架的总的观点。包含 JUnit 的装配 APIs 的主要目的是使 Java 单元测试用例的编写更快更简单。最低限度,一个 JUnit 测试用例有一个如 列表 1代码中所示的常用结构。

列表 1. JUnit 常用结构

1. Import junit.framework.TestCase;
2.
3. Public class AddJavaTest extends TestCase {
4.
5. protected void setUp() throws Exception
6. {
7. // create some object
8. }
9. protected void tearDown() throws Exception
10. {
11. //release any recourse that was created in
12. setup()
13. }
14. public AddJavaTest (String name){
15. super (name);
16.
17. public void testSimpleAddition (){
18. assertTrue (expect == actual);
19. }
}

如 列表 1 第三行所示,所有的 Java? 测试用例类都必须扩展junit.framework.TestCase ,它是 JUnit 的核心类。在第五行中,TestCase.setUp() 被重载,从而对测试下的对象进行初始化或者实例化。相反,在第九行中, TestCase.tearDown() 被重载,是释放任何定位资源的。在第十四行中,为了在日志中显示这个测试用例的名称,一个测试用例必须有一个可以将自变量传递到它的父类 (TestCase) 的专门的串参数构造器。

测试用例必须声明为 public void ,并且不带行参。此外,合适的是测试方法名称都有一个 "test" 前缀,这样测试运行人员就可以自动地执行所有的方法。最后,在第十八行的断言语句中,决定了这个测试用例的成功与失败。这个方法 assert在这个特殊的测试情景下,将期望值与实际值进行了对比。您可以利用 fail() 方法强迫这个测试用例失败,例如您想要迫使一个操作暂停。JUnit 提供了一个额外的决定测试用例成功或者失败的机制。表格 1 显示了不同 assert 和 fail 方法签名的范例。

表格 1. Assert 方法



 注意: Assert 类包含许多不同的重载方法。

JUnit 提供了一个 TestRunner 类来执行测试用例。其实测试用例的方法多种多样。测试报告是利用图形和文本来显示的。要获取最受欢迎的图形结果,可以利用 junit.swingui.TestRunner 和 junit.awtgui.TestRunner。其次受欢迎的基于文本的结果,可以 利用 junit.textui.TestRunner 来取代。

此外, JUnit 测试用例可以从 Rational Application Developer 的IDE 运行或者在 Ant 构成脚本中自动运行。

JUnit 还通过利用 junit.framework.TestSuite 类提供了一种将测试归组为测试套件的方法。一个测试套件是由您能够在相同时期运行的相关测试组成的。下面有两种 JUnit 运行测试比较便利的方法:

对于第一种方法,您可以通过使用这个命令将测试用例传递到 TestSuite 构造器:
 TestSuite suite= new TestSuite(testAddJava.class) 在这种情况下,TestRunner 将所有带测试前缀的测试程序挑选出来,然后自动运行每一个测试用例。
另一个可选的方法是,通过使用 TestSuite.addTest 方法添加每个测试用例:
 TestSuite suite = new TestSuite(); suite.addTest(new AddJavaTest("testSimpleAddition"));

 JUnitEE 测试框架

JUnit 提供了一个对客户端 Java 应用软件进行单元测试的有效且简便的方法,但是它仍有一些局限性;因此,在每一个应用软件服务器的容器中进行测试变成了一个冗长且乏味的过程。IBM Rational Application Developer 平台提供的 Web-based Universal Test Client (UTC) 特性为 Enterprise JavaBeans (EJBs) 提供了一个无缝的完整的单元测试机制。然而,Rational Application Developer UTC 是一个交互式的单元测试机制,因此当进行自动单元测试时它显得有些不足。

JUnitEE 测试框架就是解决这些局限性的,同时还处理这个冗长而乏味的过程。这个框架延伸了标准的 JUnit,从而使它能够在应用服务器容器中执行单元测试。它在单元测试软件的 J2EE Web 模块中配置,利用 TestRunner 输出 HTML 或者 XML 测试结果。它还包含一个登陆到 JUnit 测试用例登陆口的 TestServlet。因此,根据 JUnitEE.org 的说法, 将您的测试工具构建为一个标准的 J2EE Web 有以下几点好处:

测试在 J2EE Web 模块中已经打包(以 WAR 文件的形式),这样便于部署和执行。
测试用例看起来就像产品代码,对于您的 EJB, 通过 facade 模式,它们可以使用相同的 Java bean。
通过使用一个 Ant 脚本可以使测试自动化。

利用 JUnit 和 JUnitEE 进行单元测试的例子

这个部分向您展现了如何利用 JUnit 和 JUnitEE 测试框架的优势对一个在 Rational Application Developer IDE 中开发的简单的无状态 session EJB 进行单元测试。这个 EJB 仅仅添加了两个数字就返回这些数字的总和。尽管这只是一个微不足道的功能,但是它包含了阐明这个例子的所有必要条件。

简单地说,这个例子是一个无状态 session bean 的单元测试。然而,您也可以利用 JUnit 和 JUnitEE 测试框架对其它类型的 EJB 进行单元测试。这里这个简单的方法包含以下几个步骤:

  • 创建一个简单的 Java bean 计数器。
  • 为基本的计数器创建一个 EJB。
  • 创建一个 EJB 客户。
  • 开发 JUnit 测试用例对 Java 应用软件和这个计数器 EJB 进行测试。
  • 创建一个 JUnitEE 测试模块,并对它进行配置。
  • 在 WebSphere Application Server 环境中部署并执行测试用例。

您可以下载这篇文章中所有例子的源代码。

步骤 1. 创建一个简单的 Java bean 计数器

第一个例子是一个简单的 Java 应用软件,它添加了两个数字并返回了它们的和。代码 列表 2 显示了这个类执行。

列表 2. 基本的 Calculator Java Bean 的执行

package calc;

public class BasicCalculator {

public double addTwoNumbers(double first, double second) {
return first + second;
}
}

步骤2. 为基本的计数器创建一个 EJB

利用 Rational Application 开发者产生一个无状态 bean 称作 BasicCalculator。

代码列表 3 包含一个 EJB 即将呈现的样例。

BasicCalculatorBean EJB 类包含一个业务方法,addTwoNumbers,服务于计数器功能的。 这个业务方法添加在一个远程的接口中,使客户能够访问这个方法。EJB 可以是远程的、分布的对象。Rational Application Developer IDE 产生的客户端工件能够很容易的对远程目标进行访问。测试应用软件可能在一个分离的 Enterprise Archive (EAR) 中,因此我们利用远程而不是本地接口来访问 EJB。(本系列的第 3 部分显示了如何利用 IDE 的智能指南创建客户端工件)。

 列表 3. Basic Calculator EJB 的实现

package ejbs;
/**
* Bean implementation class for Enterprise Bean: BasicCalculator
*/
public class BasicCalculatorBean implements javax.ejb.SessionBean {
private javax.ejb.SessionContext mySessionCtx;

public javax.ejb.SessionContext getSessionContext() {
return mySessionCtx;
}
public void setSessionContext(javax.ejb.SessionContext ctx) {
mySessionCtx = ctx;
}
public void ejbCreate() throws javax.ejb.CreateException {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void ejbRemove() {
}

public double addTwoNumbers(double first, double second) {
return first + second;
}

}

步骤 3. 产生一个 EJB 客户端

在这个步骤中,按照 Rational Application Developer 的智能指导产生客户端工件。这些步骤在这个文章系列的第 3 部分将会有示范。

步骤 4. 开发 JUnit 测试用例来测试 Java 和计数器 EJB

当您实现完 Java Bean Calculator、 Basic Calculator EJB 以及客户 EJB 之后,您就可以编写 JUnit 测试用例了。

利用 JUnit 对 Java 应用软件进行单元测试
这里,您可以利用 列表 1 中概述的 JUnit 测试结构的优势来编写三种对 BasicCalulator Java Bean 和 EJB 进行单元测试的方法。

列表 4 中的单元测试列举了各种断言方法来执行正面与反面的单元测试。它有以下几种测试方法:

testSimpleAddition() 方法包含两个论断。第一个宣称这个计数器实例对象不是空,就像它实际存在一样。第二个验证通过与期望值 4 相比较,这个计数器准确地将2加上2。

 testSimpleAdditionNotSame() 方法展示了负面的测试,确保两个截然不同的值是不一样的。也就是说,这两个数字(2+2)的和不是等于5的。

 testDesignedToFail() 方法论证了 JUnitEE 框架中一个失败的测试用例看起来是什么样的。在这个测试用例中,两个自变量,1和1是通过BasicCalculator 来相加的,然后再将所得的值与3相比较。这个测试失败了。结果向您展示了在哪里以及为什么失败了。

列表 4. Calc Java Bean 的 JUnit Test

package calc.test;

import calc.BasicCalculator;
import junit.framework.TestCase;


public class BasicCalculatorTest extends TestCase {

BasicCalculator aBasicCalculator = null;

/*
* Setup before each test case
*/
protected void setUp() throws Exception {
super.setUp();
aBasicCalculator = new BasicCalculator();
}
public void testSimpleAdditionNotSame() throws Exception {

double result = aBasicCalculator.addTwoNumbers(2, 2);
assertNotSame("2+2 does not = 5", new Double(result), new Double(5));
}

public void testDesignedToFail() throws Exception {
double result = aBasicCalculator.addTwoNumbers(1, 1);
assertTrue("1 + 1 = 3", result == 3);

}

public void testSimpleAddition() throws Exception {

assertTrue("Calculator instance is not null", aBasicCalculator != null);

double result = aBasicCalculator.addTwoNumbers(2, 2);
assertTrue("2+2=4", result == 4);

}
}

列表 5. 测试 Basic Calculator EJB 的 JUnit 源代码

package calculator.test;

import java.rmi.RemoteException;
import com.ibm.etools.service.locator.ServiceLocatorManager;

import ejbs.BasicCalculator;
import ejbs.BasicCalculatorHome;
import junit.framework.TestCase;

public class BasicCalculatorTest extends TestCase {

BasicCalculator aBasicCalculator = null;

/*
* Setup before each test case
*/
protected void setUp() throws Exception {
super.setUp();
aBasicCalculator = createBasicCalculator();
}

protected void tearDown() throws Exception {
aBasicCalculator = null;
}

public void testSimpleAddition() throws Exception {

assertTrue("Calculator instance is not null", aBasicCalculator != null);

double result = aBasicCalculator.addTwoNumbers(2, 2);
assertTrue("2+2=4", result == 4);

}

public void testSimpleAdditionNotSame() throws Exception {

double result = aBasicCalculator.addTwoNumbers(2, 2);
assertNotSame("2+2 does not = 5", new Double(result), new Double(5));
}

public void testDesignedToFail() throws Exception {

double result = aBasicCalculator.addTwoNumbers(1, 1);
assertTrue("1 + 1 = 3", result == 3);

} /*
* Rational generated code snippet to access EJB
*/

private BasicCalculator createBasicCalculator() {

BasicCalculatorHome aBasicCalculatorHome =
(BasicCalculatorHome) ServiceLocatorManager
.getRemoteHome(STATIC_BasicCalculatorHome_REF_NAME,
STATIC_BasicCalculatorHome_CLASS);
try {
if (aBasicCalculatorHome != null) {
return aBasicCalculatorHome.create();
}
} catch (javax.ejb.CreateException ce) {
ce.printStackTrace();
} catch (RemoteException re) {
re.printStackTrace();
}
return null;
}

private final static String STATIC_BasicCalculatorHome_REF_NAME = "ejb/BasicCalculator";

private final static Class STATIC_BasicCalculatorHome_CLASS = BasicCalculatorHome.class;
}

利用 JUnit 对 EJB 进行单元测试

 由于 EJB 都是由它们的容器管理的,所以它们必须在 JNDI 定位中可以查找到。单元测试可以是一个冗长乏味且艰难的过程。幸运的是, Rational Application Developer 提供了一些为开发者执行创建一个 EJB 实例任务的代码片段。

无状态 session EJB 的计数器方法与 Java bean 的执行是一样的。因此,您可以为您的 EJB 单元测试重新使用基本的 JUnit 测试用例。但是您必须在 set() 方法中稍做变动,来对 EJB 对象进行定位和实例化。 您是否还记得一个 set() 方法的主要目的是对测试下的目标进行初始化。在这个方法中,需要调用 createBasicCalculator(),它返回了这个计数器的 EJB 实例。这个方法中的代码是由 Rational Application Developer 产生的。这个方法显示在 列表 6中。

列 6. 查找和访问 EJB 主页

private BasicCalculator createBasicCalculator() {

BasicCalculatorHome aBasicCalculatorHome =
(BasicCalculatorHome) ServiceLocatorManager
.getRemoteHome(STATIC_BasicCalculatorHome_REF_NAME,
STATIC_BasicCalculatorHome_CLASS);
try {
if (aBasicCalculatorHome != null) {
return aBasicCalculatorHome.create();
}
} catch (javax.ejb.CreateException ce) {
ce.printStackTrace();
} catch (RemoteException re) {
re.printStackTrace();
}
return null;
}

private final static String STATIC_BasicCalculatorHome_REF_NAME = "ejb/BasicCalculator";

private final static Class STATIC_BasicCalculatorHome_CLASS = BasicCalculatorHome.class;
}

步骤 5. 创建一个 JUnitEE 测试模块并对它进行配置

在 WebSphere Application Server 软件中执行您的 JUnit 测试用例,您可以利用 JUnitEE 框架的优势。正如前面所提到的,JUnitEE JUnit 框架的延伸,它在应用软件服务器中执行测试,并利用 TestRunner 界面以 HTML 或者 XML 的形式来显示结果。这个部分展示了如何利用两个测试框架创建一个单一的可部署的且包含您的测试和一个 JUnitEE 测试服务程序的 Web archive (WAR) 文件来运行您的 Java 和 EJB 单元测试用例。

在 Rational Application Developer 软件中创建一个 JUnitEE 测试模块是一个凭直觉的过程。

首先,在 Rational Application Developer 中创建一个动态的 Web 项目,命名为 BasicAddJUnitEEWeb。
通过将 junit.jar 和 junitee.jar 文件放置在 Web 项目的 WEB-INF/lib 位置,把它们放置在这个项目的类路径。
创建一个 jar 文件,命名为 BasicAddUnitTest.jar,包含这两个测试用例类。拷贝这个 jar 文件,BasicAddUnitTest.jar 到 WEB-INF/lib 位置。
包括以 web.xml 部署描述符形式显示在列表 7中的 XML 代码。

列表 7. 在部署描述符 web.xml 中的 JUnitEE 测试 servlet 的映射

1. <servlet>
2. <servlet-name>JUnitEEServlet</servlet-name>
3. <display-name>JUnitEEServlet</display-name>
4. <servlet-class>org.junitee.servlet.JUnitEEServlet
5. </servlet-class>
6. <init-param>
7. <param-name>searchResources</param-name>
8. <param-value>BasicAddUnitTest.jar</param-value>
</init-param>
9. </servlet>
10. <servlet-mapping>
11. <servlet-name>JUnitEEServlet</servlet-name>
12. <url-pattern>/TestServlet/*</url-pattern>
</servlet-mapping>

JunitEE 测试用例能够在这个应用服务器环境中以智能的或者基本的模式来执行。使用智能的模式, JUnitEE TestRunner 可以自动对以 "Test" 或者 "Tests" 的测试类进行定位。智能的模式通过以部署描述符的形式对 JAR 文件具体命名来进行配置,如 列表 7中第6—8行所示。如果不止一个 JAR 文件,就用逗号来分隔。在 Servlet 初始化的过程中,这个容器可以通过一次调用 init 方法来激活这个 Servlet。这个 init 方法为 Servlet 实例初始化了一套参数。这个参数名 searchResources 在第七行中已经声明,在第八行中, BasicAddUnitTest.jar 的值分配给这个参数。因此,无论这个容器何时激活这个 JunitEEServlet, JUnitEE 都会在 BasicAddUnitTest.jar 文件中查找所有的测试类。

智能测试模式的备选方案是基础测试模式。在基础测试模式中,TestRunner 仅仅执行详细列在 WEB-INF/testCase.txt 文件中的测试类,它是一个每行只包含一个 JUnit 测试用例名称的简易文件,如列表 8所示。

列表 8. 带有 JUnit 测试包和类名称的测试文件

com.ibm.junitee.sample.operation.test.BasicAddJavaTest
com.ibm.junitee.sample.operation.test.BasicAddEJBTest

在最后的步骤中,部署之前,确保构建路径和所有的 JAR 依存关系都在适当的位置。此外,确保 EAR 文件包含所有 JAR 文件的必要效用, JUnitEE Web 模块也一样要打包成一个 WAR 文件包。当完成这些之后,继续进入到下一个部分在 WebSphere Application Server 环境中部署您的测试。

步骤 6.在 WebSphere Application Server 中部署并执行测试用例

到目前为止,您已经完成了所有的基本工作,您拥有一个包含您的配置好并打包的 JUnitEE 测试模块的 EAR 文件 (是一个 WAR 文件)。在 Rational Application Developer IDE 中部署一个 EAR 文件也是一个凭直觉的过程,因此它将有益于稍后对这个系列的第 3 部分中的逐步示范的学习。

当您在应用服务器上部署完 EAR 文件以后,打开您的浏览器并指向 JUnitEE Servlet 的 URL 地址。这个 URL 地址的形式是 http://<hostname>:<portnumber>/BasicAddJUnitEEWeb/TestServlet, 其中 BasicAddJUnitEEWeb 是 Web 上下文的名称,动态 Web 项目的名称和 TestServlet 是 JUnitEE TestRunner servlet。这个例子使用了 http://localhost:9080/BasicAddJUnitEEWeb/TestServlet。 图 1 显示了信息是如何在 J2EE 组件间交换的概念视图。

图 1. 组件的交互


 一次成功的运行将在 JUnitEE TestRunner 窗口中显示所有的测试,如 图 2 所描述的。这个屏幕显示了执行您的单元测试的不同方法。选择您喜欢的测试选项或者键入您测试套件的名称到适当的字段来开始运行测试用例。

图 2. JUnitEE TestRunner 屏幕


 如果配置或者 JNDI 名称查找都没有错误,test runner 就执行被选的测试并将结果显示在 JUnit Test Results 窗口。这个窗口提供了关于执行测试的综合信息。如果测试通过,它将用一个绿色的复选标记来表示。如果失败了,则用一个红色的 X 来表示。点击这个 X 则提供一些供调试用的线索信息。我们故意促成一个测试失败,观察 JUnit 如何来显示一个失败的测试,如 图 3所描述。

图 3. 失败测试的 JUnit Test Results 视图


 结论

这篇文章中的例子展示了您如何通过利用 IBM 技术和开源的 JUnit 以及 JUnitEE 测试框架来交付准确的、高质量的 J2EE 应用软件,从而能够快速且容易地对 Java 和 EJB 应用软件的单元测试进行开发,配置以及执行。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号