开发人员:J2EE
利用 Oracle 实体测试工具进行容器外的 EJB 3.0 测试
作者 Debu Panda
了解 Oracle 的实体测试工具如何使得利用 EJB 3.0 实体组件进行测试驱动的开发变得更容易。
很多软件项目由于缺少测试而无法提供高质量的产品。测试经常是事后想法或者在项目交付时间紧迫时被削减。此外,开发人员认为他们只负责开发应用程序模块而不负责应用程序的总体 质量 。结果是他们没有为其创建的模块构建测试用例。
测试驱动的开发是 eXtreme 规划的一个核心工作,它通过使应用程序测试成为一项要求而非事后想法,彻底改变了面向对象的应用程序的开发。它要求开发人员为其构建的每个类设计单元测试。很多工具、实用程序和框架 — 如 JUnit 和 HttpUnit — 可以使 Java 开发人员的这项工作变得更容易,这些开发人员在过去几年中已经采用了测试驱动的开发。
近年来避免使用企业 JavaBeans (EJB) 的主要原因之一是因为 EJB 组件不能进行方便的单元测试。幸运的是,EJB 3.0 规范极大地改变了编程模型,有可能使 EJB — 主要是实体 — 能够在容器外使用和测试。
在本文中,我将解释测试驱动的开发的益处以及 EJB 3.0 如何能够使您在容器外测试 EJB。我还会介绍随 Oracle 应用服务器所提供的实体测试工具如何实现 EJB 3.0 实体的测试。
为什么要进行测试驱动的开发?
测试驱动开发要求您在开发一个类之前先构建单元测试,从而提高软件质量。这种方法提供了以下的好处:
测试变成为用例,并能够验证系统设计。
应用程序代码变成模块化,因此可以独立地测试每个模块。
当交付应用程序时,将提供自动化的单元测试。
为应用程序所开发的测试可以用作系统文档。
使用 EJB 2.x 进行的测试驱动开发
EJB 是在部署在 EJB 容器和并在其中执行的分布式组件。因为 EJB 需要一些由 J2EE 服务器提供的资源来执行其所要完成的任务,所以在容器外无法方便地进行测试。以下是这种测试为何难以完成的一些更多原因:
EJB 不是纯 Java 类,并且它们具有生命周期方法,因此必须有运行时环境。
EJB 方法不能直接调用,需要使用 JNDI 来查找和创建实例。
EJB 需要诸如事务处理和数据源等服务来执行。
不过,对容器管理持久性 (CMP) 实体组件进行单元测试仍然很困难,因为 CMP 实体组件是抽象类,前端由数据转移对象和会话组件外观 cmp 组成。CMP 实体组件的 O-R 映射没有实现标准化,因此实体组件要绑定到部署平台,在供应商专用的部署描述符中提供映射。
有些开发人员已经使用其他方法来完成 EJB 测试的自动化 — 如利用 Apache Cactus 进行容器内测试或者利用轻型框架进行容器外测试 — 但是这些方法很复杂,阻碍了它们的广泛使用。
容器内的 EJB 测试
这种方法要将您的 EJB 部署到 EJB 容器中。我在前面讲过,诸如 Apache Cactus 等测试框架可用于在容器内测试 EJB,但是这种方法的主要问题是测试变得很复杂且通常要依赖于容器 API。此外,如果您更改了代码逻辑,则您必须将 EJB 重新部署到容器,这是复杂而耗时的工作。
在容器中运行测试的主要问题是您必须创建一个完整的部署模块,并且必须能够配置和启动服务器 — 经常涉及到其他组件和不相关的配置以及必需的程序包 — 还必须部署应用程序模块。
利用轻型框架进行容器外的 EJB 测试
诸如 MockEJB 等框架允许您在容器外测试 EJB。但是,这些框架不提供多项选择。它们并不始终与最新的 EJB 规范同步,并且有许多其他的限制。会话组件可能是利用这些框架进行容器外测试的上佳对象,但是由于实体组件提供复杂的服务,不可能始终都能够测试它们。EJB 2.x 实体组件是抽象类,过于依赖容器来生成持久性逻辑,如容器管理关系;因此,构建这样的框架与构建另一个应用服务器一样复杂。
EJB 3.0 规范:开发人员的希望
EJB 3.0 极大地简化了编程模型。当前的 EJB 类似一个普通的 Java 类,通常称为 POJO(普通的旧式 Java 对象),而组件类现在将其 作为业务接口来实施。 .EJB 类不必实施 java.ejb.EnterpriseBean 接口; EJB 可以选择不使用部署描述符,转而使用批注。
EJB 3.0 还简化了 CMP 实体组件的 API,并在 POJO 持久模型的基础上进行标准化,与 Oracle TopLink 相类似。以下是持久性 API 中的一些主要变化,它们允许您利用轻型框架在容器外测试实体:
当前的实体为具体的类和 POJO。
实体 Bean 不需要接口,并且使用 EntityManager API 来执行创建、读取、更新、删除 (CRUD) 操作。
O-R 映射已经完成了标准化,且消除了对复杂的供应商描述符的依赖。
从 API 中去除了容器管理关系。
在过去,实体 Bean 的单元测试是一项令人生畏的任务。EJB 3.0 使实体 Bean 测试变得更容易;因为 EJB 3.0 类似于 POJO,所以测试这些类非常容易。
实体 Bean 用于持久性,因此需要访问数据库并支持数据源对象。在 EJB 3.0 中仍然需要 JNDI 支持,因为需要获取 EntityManager 的一个实例,该实例用于实体的 CRUD 操作。仍旧需要事务处理支持,因为必须在事务处理上下文中调用 EntityManager API。
Oracle Test Harness 好处
Oracle 的实体测试工具使您能够:
在 EJB 容器外测试实体,而不必将模块部署到 EJB 容器。
在容器外测试那些在容器中运行的相同代码 — 不需要容器专用的 API。
使用您喜欢的框架 — 没有将您限制在专用框架或 Oracle 专用的 API 中。
如果您的实体 Bean 拥有任何会话组件外观,则进行单元测试。
Oracle 的实体测试工具
Oracle 应用服务器中的实体测试工具是一个轻型框架,它使容器外的实体 Bean 测试变得更简单。它允许在容器外使用容器中所运行的相同代码,并且不需要 Oracle 专用的 API。
Oracle 应用服务器实体测试工具提供了实体 Bean 单元测试所需的三种基本服务:
仿真数据源
基本 JNDI
简单 JTA 功能。
这些服务极为简化;这些更适于测试,而不是部署产品应用程序所需的实际实施。
Oracle 应用服务器实体测试工具被打包在一个单独的 jar 文件 (ectest.jar) 中,并依赖于 EJB 3.0 持久性管理器,如 Oracle TopLink。它仿真以上的三种基本 J2EE 服务,允许使用这些服务的测试代码在容器外运行。您可以使用实体测试工具来选择用于测试实体的测试框架,而且不必使用任何 Oracle 专用的配置。
当您使用实体测试工具来测试您的实体和会话组件外观时,您不必对应用程序代码进行任何更改。
要使用实体测试工具在容器外测试您的实体,必须执行以下步骤:
1. 创建实体测试工具所需的配置类。 这些配置包括应用程序所使用的数据源以及要在容器外测试的实体的名称。
EntityContainer 类是实体测试工具的核心。1.应用程序不必对该类进行实例化;它们由测试环境自动实例化,因此您可以测试相同的实体 Bean。您必须为您的容器指定某些必要的配置信息,方法是从静态的 getContainerConfig() 方法返回一个 ContainerConfig 实例。该配置必须包括一个 JTA 数据源名称和一个要测试的实体 Bean 的列表。可选的属性可能包括一个会话名称和一个非 JTA 的数据源名称。
下面的示例将返回一个 ContainerConfig 实例:
package examples.ejb.cmp30.relationships;
import oracle.toplink.ejb3test.*;
import java.util.*;
public class OrderDemoSessionConfig {
public static DataSourceConfig getJtaDataSource() {
return new DataSourceConfig(
"DataSource", /* data source name */
"jdbc/OracleDS", /* jndi name */
"jdbc:oracle:thin:@localhost:1521:ORCL", /* URL */
"oracle.jdbc.driver.OracleDriver", /* jdbc driver */
"scott", /* user name */
"tiger"); /* password */
}
public static Collection getEntities() {
Vector classes = new Vector();
classes.add("examples.ejb.cmp30.relationships.Customer");
classes.add("examples.ejb.cmp30.relationships.Item");
classes.add("examples.ejb.cmp30.relationships.Order");
return classes;
}
public static ContainerConfig getContainerConfig() {
return new ContainerConfig(
null, /* session name */
getJtaDataSource(), /* tx data source config */
null, /* non-tx data source config */
getEntities() /* entities */);
}
}
实体测试工具目前只支持一个 JTA 和一个非 JTA 的数据源。
2. 构建您的测试类。 选择一个测试框架,在其中构建您的单元测试用例。这些测试类不需要容器 API。
在您的测试类中,查找并获取 EntityManager 的一个实例,如下所示:
Context ctx = new InitialContext();
EntityManager em = (EntityManager) ctx.lookup("java:comp/ejb/EntityManager");
您在使用 EntityManager API 之前可以启动一个 JTA 事务。您可能希望在测试类中创建一个方法,该方法完成 EntityManager 的初始化,并启动一个 UserTransaction。例如:
Context ctx = new InitialContext();
UserTransaction txn = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");
txn.begin();
3. 使用您选用的框架来执行您的测试类。 以下是执行该测试所需的一些设置。
定义容器配置的类必须被指定为系统属性 java.persistence.setup.config 的值,如
java -Djava.persistence.setup.config=examples.ejb.cmp30.relationships.OrderDemoSessionConfig
包含实体测试工具的类的 ectest.jar 文件必需放在 CLASSPATH 中,并且启动 JVM 时必需选用 -javaagent 选项。
例如,如果您构建了一个名为 MyTest 的测试类,它具有一个配置类 OrderDemoSessionConfig,则您必须按以下方法执行您的测试用例:
java
-Djava.persistence.setup.config="examples.ejb.cmp30.relationships.OrderDemoSessionConfig"
-javaagent:d:/myhome/toplink/jlib/ectest.jar MyTest
后续步骤
1. 下载 Oracle 应用服务器 EJB 3.0 预览版。Oracle EJB 3.0 容器允许您构建和测试 EJB 3.0 组件。
3. 在容器外测试 EJB 3.0 实体: 示例代码
相关资源:
您可以利用诸如 JUnit 或 JTestCase 等框架来构建您的测试用例。Oracle 提供了一个使用 JUnit 的示例测试用例,不过该测试工具主要在容器外测试实体,并不完全支持其他类型的 EJB。
会话组件的可测试性
Oracle 的实体测试工具还允许您测试实体 Bean 的会话组件外观。会话组件是一个简单的 Java 类,它实施其业务接口,批注其类型,并且能够通过测试框架在容器外方便地进行单元测试。当它使用 JNDI 查找或依赖注入来使用任何资源或服务时,复杂性也随之而产生。在没有一个支持实体 Bean 的测试工具时,对那些仅仅用作实体 Bean 外观的会话组件进行功能测试也很困难。
总结
EJB 3.0 简化了编程模型,使您能够在容器外测试 EJB。随 Oracle 应用服务器所提供的实体测试工具使得利用 EJB 3.0 实体 Bean 进行测试驱动开发变得更加容易,且让您能够立即开始在容器外测试 EJB 3.0 实体 Bean 。
Debu Panda 是 Oracle 应用服务器开发团队的资深产品经理,他在该团队中主要专注于 EJB 容器和事务管理器方面的工作。他在 IT 业界拥有 13 年以上的经验,在数家杂志上发表过文章,并出席了很多会议。通过他的以 J2EE 为主的 weblog 可以获得其资料。