级别: 初级
Sultan Ahamed Kaja, 高级软件工程师, IBM Global Services India
Sunil Mahamuni (mailto:msunil@in.ibm.com?subject=使用 JUnit 在 VisualAge for Java 中对 EJB 进行单元测试), 高级软件工程师和小组负责人, IBM Global Services India
2003 年 1 月 01 日
本文面向的是那些想要对他们的 EJB 进行单元测试以及为这些 EJB 开发测试案例的 VisualAge® for Java™ 用户。本文基于 VisualAge for Java 3.5.3 和 JUnit 3.7。文章描述了 JUnit、对 EJB 进行单元测试的难点以及开发测试案例时涉及到的相关步骤。
© Copyright International Business Machines Corporation 2002. All rights reserved.
引言
本文面向的是那些想要对他们的 EJB 进行单元测试以及为这些 EJB 开发测试案例的 VisualAge® for Java™ 用户。本文基于 VisualAge for Java 3.5.3 和 JUnit 3.7。文章描述了 JUnit、对 EJB 进行单元测试的难点以及开发测试案例时涉及到的相关步骤。
单元测试是以程序员的视角来编写的。单元测试确保一个类的某个特定的方法能成功地执行一组特定的任务。每个测试确定一个方法在给定已知的输入时能产生预期的输出。有效测试是有效编程的一个基本的组成部分。通过使用 JUnit 测试框架,您能容易地并且逐步地构建一个测试套件,这个测试套件能帮助您调节工作进度、发现不希望出现的副作用并把精力集中在开发工作上。
要用 VisualAge for Java 配置 JUnit,请参阅 Techniques for Implementing Tests using JUnit with VisualAge for Java一文。
编写 EJBs 测试案例
这里是一个关于 EJB 的示例,该 EJB 带有一个名为 addition
的业务方法,该方法以两个整型变量作为输入,将它们相加后返回结果:
/**
* This is a Session Bean Class
*/
public class SampleEjbBean implements SessionBean {
private javax.ejb.SessionContext mySessionCtx = null;
final static long serialVersionUID = 3206093459760846163L;
/**
* Insert the method's description here.
* Creation date: (8/10/02 1:16:33 PM)
* @return int
* @param a int
*/
//The Business method
public int addition(int a,int b) {
return a+b;
}
public void ejbActivate() throws java.rmi.RemoteException {...}
public void ejbCreate() throws javax.ejb.CreateException, java.rmi.RemoteException {...}public void ejbPassivate() throws java.rmi.RemoteException {...}
public void ejbRemove() throws java.rmi.RemoteException {...}
public javax.ejb.SessionContext getSessionContext() {
.....
}
public void setSessionContext(javax.ejb.SessionContext ctx) throws java.rmi.RemoteException {
.....
}
}
|
使用下列步骤创建一个 EJB 测试案例。
- 通过继承
JUnit.framework.TestCase
类创建一个测试类。命名约定:如果 bean 的名称是 SampleEjbBean
,则将测试类命名为 SampleEjbBeanTest
。例如:
public class SampleEjbBeanTest extends JUnit.framework.TestCase{
。
- 创建 Bean 的一个
remoteInterface
类型的类变量。例如:
SampleEjb remoteInterface
- 创建测试类的一个静态实例:
static {
instance = new SampleEjbBeanTest("");
}
|
因为该实例被用来作为 TestRunner 的 run 方法的一个参数以执行 TestClass.main 方法和测试案例,所以您可以在 SwingUI 或者 TextUI 中执行测试案例:
public static void main(String args[])
{
if (args.length > 0){
if (args[0].equals("SWING")) {
JUnit .swingui.TestRunner.run(instance.getClass());
}
else {
JUnit .textui.TestRunner.run(instance.getClass());
}
}
else {
//formatting the Output
System.out.println("************************************");
String className = instance.getClass().getName();
className = className.substring(className.lastIndexOf(".")+1);
System.out.println("Test Report of:"+className);
System.out.println("************************************");
JUnit.textui.TestRunner.run(instance.getClass());
}
}
|
- 接着,创建一个用于连接运行在服务器上的 EJB bean 的方法并为远程接口创建句柄:
- 将初始上下文添加到 HashMap 中。例如:
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.ejs.ns.jndi.CNInitialContextFactory
- 将 URL 添加到 HashMap 中。例如:
env.put(Context.PROVIDER_URL, "iiop://localhost:900");
- 创建
InitialContext
对象。例如:
javax.naming.InitialContext ic =new javax.naming.InitialContext(env);
- 通过在命名服务器中查找 EJB Alias 名称来构造 Bean 的一个
homeInterface
例如:
SampleEjbHome homeInterface = (SampleEjbHome) ic.lookup("SampleEjb");
- 通过调用
homeInterface
的 create
方法创建一个 remoteInterface
。 例如:
remoteInterface = homeInterface.create();
Public void getConnection()
{
getinfo("Running " + this.toString());
java.util.Hashtable env = new Hashtable();
//Adding the Initial Context to the HashMap.
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.ejs.ns.jndi.CNInitialContextFactory
");
env.put(Context.PROVIDER_URL, "iiop://localhost:900");
try
{
getinfo("Creating an initial context");
javax.naming.InitialContext ic =new javax.naming.InitialContext(env);
getinfo("Looking for the EJB " + "SampleEjb");
SampleEjbHome homeInterface =
(SampleEjbHome) ic.lookup("SampleEjb");
getinfo("Creating a new EJB instance");
remoteInterface = homeInterface.create();
}
catch (NamingException e)
{
getinfo(e.toString());
fail();
}
catch (Exception e)
{
getinfo("Create Exception");
getinfo(e.toString());
fail();
}
}
|
- 在用于建立固定设备(fixture)(例如,打开一个网络连接,打开一个文件)的
setup
方法中,这个方法在测试程序执行前被调用。调用 getConnection()
方法使得测试案例连同 EJB 设置一起得以初始化。
public void setUp() throws Exception
{
......
/*Invoking the getConnection method*/
getConnection();
}
|
- 为方法创建测试案例。使用下列命名约定:将测试案例命名为
testXXXX(testAddition)
。在 testMethod(testAddition)
中,从 remoteInterface
对象调用业务方法( addition
)。例如:
int result=remoteInterface.addition(10,15);
public void testAddition() {
try{
int result=remoteInterface.addition(10,15);
getinfo("----------------------------------");
getinfo("Success without Exception");
assertEquals(25,result);
}
catch(Exception e){
fail("Fail"+e);
System.out.println("Error"+e);
}
}
|
- 为报告而开发的其他方法:
public void getinfo(boolean msgObj)
{
System.out.println(msgObj);
}
public void getinfo(String msg)
{
System.out.println(msg);
}
|
执行测试案例
- 部署 EJB。
- 在 VisualAge for Java 上启动 WebSphere 测试环境(WebSphere Test Environment)和持久名称服务器(Persistence Name Server)。
- 将 EJB 添加到服务器配置中。
- 启动 EJB 服务器。
- 在 VisualAge for Java 中调用
main
方法执行测试类。以下为运行结果:
************************************
Test Report of: SampleEjbBeanTest
************************************
. Running testAddition(com.prudential.retserv.chart.servlet.SampleEjbBeanTest)
Creating an initial context
Looking for the EJB SampleEjb
Creating a new EJB instance
----------------------------------
Success without exception
Time: 15.984的
OK (1 tests)
|
通过此方法,您能开发测试案例并从 EJB 被部署的地方对该 EJB 进行单元测试。
对 EJB 进行单元测试时的问题
单元测试在单独地、隔离地并快速地运行时,运行状况最佳。一个测试案例通常会构造它正在测试的对象。然而,有时这个正在被测试对象要依赖于其他对象的行为。在这些情况下,测试案例会建立一个测试工具以“模拟”所需的行为。这个测试工具与真实系统有同样的接口,但实际上却不做任何事,它“欺骗”被测试的单元使它相信是系统在测试它。EJBs 通常依赖于它们的容器所提供的上下文,并且如果没有那些上下文,大多数的 EJBs 将不会起作用。测试案例必须从执行该 EJB 的容器中调用 EJB。您也可以在 EJB 对象所在的本地环境中测试它们,但这样做可能使测试的正确性大打折扣。所以,所开发的测试案例应该在部署了 EJB 并且正在执行 EJB 的容器中调用 EJB。
关于 JUnit 的附加信息
- JUnit 测试能让您更快地开发出质量更高的代码。
- JUnit 测试检查它们自身的运行结果并提供及时的反馈。
- JUnit 测试能被组织成呈层次结构的多个测试套件。
- 您能快捷地并廉价地编写 JUnit 测试案例。
- 开发人员能很容易地完成 JUnit 测试。
- JUnit 测试是用 Java 写成的。
- JUnit 能测试 EJBs,servlets 和线程程序。
- JUnit 是围绕两种主要的设计模式而设计的:命令模式和复合模式。
- JUnit 是免费的。
JUnit 不具备的功能
- JUnit 不会从测试代码中分离出测试数据。
- JUnit 不能对 Swing GUIs, JSPs 或者 HTML 进行单元测试。
- JUnit 测试不能取代功能测试。即使您所有的单元测试都成功,也并 不意味着你的软件一切就绪。
- JUnit 不能单元测试服务器端的 Java 代码。几种 Xunit 测试框架被设计用来单元测试服务器端的 Java 代码。
结束语
单元测试非常重要。它们不仅能减少开发时间更能提高代码质量。在 EJB 代码被开发前首先将单元测试写成一个套件,这些单元测试将提供一个强有力的确认与回归工具。JUnit 能使单元测试案例的编写工作变得更轻松。
相关信息