当前页面: 开发资料首页 → J2EE 专题 → 用Cactus来测试J2ee应用
摘要: cactus j2ee xp 测试
韩伟 (java_cn@21cn.com)
北京某公司系统分析员
2002 年 8 月
Junit是当前最流行的测试框架,它能够让开发人员很方便的编写测试单元,可以使他们"放心"地开发。但是现在很多的应用都是基于j2ee的,代码都是在服务器端的容器里面运行,这个使测试带来了一些麻烦。对于普通的jsp,servlet用Junit来测试好像已经不是那么方便,对于EJB来说,特别是2.0版本,很多接口都是Local Interface,没有办法进行分布式的测试。那么我们如何进行这些代码的测试呢?Apache为我们提供了一个强大的工具 Cactus!它是一套简单,易于使用的服务器端测试框架,可以使开发人员很轻松的测试服务器端的程序,他们会说:"哦,就是这么简单"。Cactus是Junit的一个扩展,但是它又和Junit有一些不同。Cactus的测试分为三种不同的测试类别,JspTestCase,ServletTestCase,FilterTestCase,而不是像Junit就一种TestCase。Cactus的测试代码有服务器端和客户端两个部分,他们协同工作。那我们为什么不用Junit来做测试呢?主要有一下几个理由:
前面是对Cactus作了一个大致的介绍,接下来我们用一个实际的例子来运用一下这个强大的测试框架。首先我们需要一个被测试的对象,在这里我们选用EJB2.0 CMP.我们做一个简单的用户管理。一下就一些主要的代码,来进行一些分析。
UserHome.java package usersystem; import javax.ejb.*; import java.util.*; public interface UserHome extends javax.ejb.EJBLocalHome { public User create(String name, String password) throws CreateException; public Collection findAll() throws FinderException; public User findByPrimaryKey(String name) throws FinderException; } User.java package usersystem; import javax.ejb.*; import java.util.*; public interface User extends javax.ejb.EJBLocalObject { public String getName(); public void setPassword(String password); public String getPassword(); public void setUserInfo(UserInfo userInfo); public UserInfo getUserInfo(); public void setName(String name); } UserInfoHome.java package usersystem; import javax.ejb.*; import java.util.*; public interface UserInfoHome extends javax.ejb.EJBLocalHome { public UserInfo create(String name, String email, String address, String tel) throws CreateException; public UserInfo findByPrimaryKey(String name) throws FinderException; }
这里有两个Entity Bean用来创建用户信息。他们之间的关系在xml部署描述文件中描述,他们是1对1的关系。
UserManagerLocal.java package usersystem; import javax.ejb.*; import java.util.*; public interface UserManagerLocal extends javax.ejb.EJBLocalObject { public void addUser(String name, String password, String email, String address, String tel); public Collection findAll() ; public void delAll(); public void delByName(String name); public User findByName(String name) ; } UserManagerBean.java package usersystem; import javax.ejb.*; import javax.rmi.PortableRemoteObject; import javax.naming.*; import java.util.*; public class UserManagerBean implements SessionBean { SessionContext sessionContext; public void ejbCreate() throws CreateException { /**@todo Complete this method*/ } public void ejbRemove() { /**@todo Complete this method*/ } public void ejbActivate() { /**@todo Complete this method*/ } public void ejbPassivate() { /**@todo Complete this method*/ } public void setSessionContext(SessionContext sessionContext) { this.sessionContext = sessionContext; } /** * 添加用户 * @param name 用户姓名 * @param password 密码 * @param email 电子邮件 * @param address 地址 * @param tel 电话 */ public void addUser(String name, String password, String email, String address, String tel) { try{ UserHome userHome=getUserHome(); User user=userHome.create(name,password) ; //create user entity UserInfoHome userInfoHome=getUserInfoHome(); UserInfo userInfo=userInfoHome.create(name,email,address,tel) ;// create userinfo entity user.setUserInfo(userInfo) ; }catch(Exception e){ throw new javax.ejb.EJBException (e.toString()); } } /** * 返回UserHome接口 * @return userHome */ private UserHome getUserHome(){ try { javax.naming.InitialContext ctx=new javax.naming.InitialContext (); Object ref = ctx.lookup("User"); //cast to Home interface UserHome userHome = (UserHome) PortableRemoteObject.narrow(ref, UserHome.class); return userHome; } catch (ClassCastException ex) { ex.printStackTrace() ; return null; }catch (NamingException ex) { ex.printStackTrace() ; return null; } } /** * 返回UserInfoHome接口 * @return */ private UserInfoHome getUserInfoHome(){ try { javax.naming.InitialContext ctx=new javax.naming.InitialContext (); Object ref = ctx.lookup("UserInfo"); //cast to Home interface UserInfoHome userInfoHome = (UserInfoHome) PortableRemoteObject.narrow(ref, UserInfoHome.class); return userInfoHome; } catch (ClassCastException ex) { throw new EJBException(); }catch (NamingException ex) { throw new EJBException(ex.toString()); } } /** * 返回所有用户记录 * @return c * @throws javax.ejb.FinderException */ public java.util.Collection findAll() { Collection c = null; try { UserHome uh=this.getUserHome() ; c=uh.findAll() ; } catch (FinderException ex) { throw new javax.ejb.EJBException (); } return c; } /** * 删除所有记录 */ public void delAll(){ try { UserHome u=getUserHome(); java.util.Collection c=u.findAll() ; java.util.Iterator i=c.iterator() ; while(i.hasNext() ){ u.remove(((User)i.next()).getName()) ; } } catch (Exception ex) { throw new EJBException(ex.toString()); } } /** * 根据用户名删除记录 * @param name */ public void delByName(String name) { try { User user=findByName(name); UserHome uh=getUserHome(); uh.remove(user.getName()) ; } catch (Exception ex) { throw new javax.ejb.EJBException (ex.toString()); } } /** * 通过用户名查找用户记录 * @param name * @return */ public User findByName(String name) { try { UserHome uh=this.getUserHome() ; User user=(User)uh.findByPrimaryKey(name) ; UserHome u=this.getUserHome() ; User uu=u.findByPrimaryKey(name) ; return user; } catch (FinderException ex) { throw new EJBException(ex.toString()); } } }
UserManagerBean是一个session bean ,它主要是对user的管理,和客户端通讯,其实就是session facade模式 。代码里面有注释,这里就不多叙述了。
ejb-jar.xml 部署文件描述
<?xml version="1.0" encoding="UTF-8"?> ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">UserManager UserManager usersystem.UserManagerLocalHome usersystem.UserManagerLocal usersystem.UserManagerBean Stateless <transaction-type>Container</transaction-type>User Entity usersystem.UserHome usersystem.User User UserInfo Entity usersystem.UserInfoHome usersystem.UserInfo UserInfo User User usersystem.UserHome usersystem.User usersystem.UserBean Container java.lang.String False 2.x User name password name findAll select Object(theUser) from User as theUser UserInfo UserInfo usersystem.UserInfoHome usersystem.UserInfo usersystem.UserInfoBean Container java.lang.String False 2.x UserInfo name address tel name userInfo-user userInfo UserInfoRelationshipRole One userInfo UserInfo user user user UserRelationshipRole One user User userInfo userInfo <trans-attribute>Required</trans-attribute> User * <trans-attribute>Required</trans-attribute> UserManager * <trans-attribute>Required</trans-attribute> UserInfo *
接下来是访问EJB的客户端,我们用了一个servlet.
ManaServlet.java package usersystem.servlet; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import usersystem.*; import javax.naming.*; import javax.ejb.*; import javax.ejb.*; import javax.ejb.*; /** *Title:
*Description:
*Copyright: Copyright (c) 2002
*Company:
* @author unascribed * @version 1.0 */ public class ManaServlet extends HttpServlet { static final private String CONTENT_TYPE = "text/html; charset=GBK"; private UserManagerLocalHome h=null; private UserManagerLocal uml=null; public void init() throws ServletException{ try { h=getHome(); uml=h.create() ; } catch (CreateException ex) { ex.printStackTrace() ; } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public void addUser(HttpServletRequest request, HttpServletResponse response) throws javax.ejb.EJBException { String name=request.getParameter("name") ; String tel=request.getParameter("tel") ; String address=request.getParameter("address") ; String email=request.getParameter("email") ; String pass=request.getParameter("pass") ; uml.addUser(name,pass,email,address,tel) ; } public User findByName(String name) throws javax.ejb.EJBException { User u = null; u=uml.findByName(name) ; return u; } public java.util.Iterator findAll() throws javax.ejb.EJBException { java.util.Collection c=uml.findAll() ; return c.iterator() ; } public void delAll() throws javax.ejb.EJBException { uml.delAll() ; } public void delUser(String name) throws javax.ejb.EJBException { uml.delByName(name) ; } public UserManagerLocalHome getHome() { UserManagerLocalHome home = null; try { javax.naming.InitialContext ctx=new javax.naming.InitialContext (); home=(UserManagerLocalHome)ctx.lookup("UserManagerLocal") ; } catch (NamingException ex) { ex.printStackTrace() ; return null; } return home; } public void destroy() { } }
这个servlet在doGet,doPost没有实现任何方法,这个不影响我们测试,我们要测试的只是这些public method. 我们的测试代码如下:
package usersystem.test; /** *Title:
*Description:
*Copyright: Copyright (c) 2002
*Company:
* @author unascribed * @version 1.0 */ import usersystem.servlet.*; import java.io.IOException; import java.net.URLDecoder; import java.util.Hashtable; import junit.framework.Test; import junit.framework.TestSuite; import org.apache.cactus.Cookie; import org.apache.cactus.ServletTestCase; import org.apache.cactus.WebRequest; import org.apache.cactus.WebResponse; import javax.ejb.*; import javax.servlet.*; import usersystem.*; public class ManaServletTest extends ServletTestCase{ ManaServlet servlet=new ManaServlet(); public ManaServletTest(String theName) { super(theName); } public void setUp(){ try { servlet.init() ; } catch (ServletException ex) { ex.printStackTrace() ; this.fail() ; } } public void tearDown(){ } public void beginAddUser(WebRequest theRequest) { theRequest.addParameter("name", "nameValue"); theRequest.addParameter("pass","passValue") ; theRequest.addParameter("tel","telValue") ; theRequest.addParameter("address","addressValue") ; theRequest.addParameter("email","emailValue"); } public void testAddUser() throws javax.ejb.EJBException{ servlet.addUser(request,response) ; } public void testFindAll(){ java.util.Iterator i=servlet.findAll() ; //assertEquals(null,i); boolean ok=false; while(i.hasNext() ){ if(((User)i.next()).getName().equals("nameValue")) { ok=true; }; } this.assertTrue(ok) ; } public void testFindByName() throws javax.ejb.EJBException { User u=servlet.findByName("nameValue") ; UserInfo ui=u.getUserInfo() ; this.assertEquals("email",ui.getEmail()) ; this.assertEquals("tel",ui.getTel()) ; this.assertEquals("nameValue",u.getName()) ; this.assertEquals("passValue",u.getPassword()) ; } public void testDel() throws javax.ejb.EJBException { servlet.delUser("nameValue8") ; } public void testDelAll() throws javax.ejb.EJBException { servlet.delAll() ; } public static void main(String[] theArgs) { junit.textui.TestRunner.main(new String[]{ ManaServletTest.class.getName()}); } public static Test suite() { return new TestSuite(ManaServletTest.class); } }
public class ManaServletTest extends ServletTestCase 我们要测试的是一个servlet,所以我们继承ServletTestCase,如果你测试jsp的话,就继承JspTestCase.
public ManaServletTest(String theName) { super(theName); }
和junit一下,ServletTestCase不允许使用默认的构造函数,所以必须使用一个带参数的构造函数,并且调用 父类的构造函数。
public void setUp(){ try { servlet.init() ; } catch (ServletException ex) { ex.printStackTrace() ; this.fail() ; } } public void tearDown(){ }
setUp是在测试类运行时候首先被调用的办法,在这里可以进行一些数据初始化之类的工作。在这里我们调用了 servlet.init().
在测试类运行的时候需要显式的调用servlet的init()方法。因为cactus在测试servlet的时候是实例化一个ser vlet的,不会调用inti(),而servlet enginer在调用的时候是会自动调用servlet的init()方法的。tearDown方 法在测试完成的时候运行,进行一些必要的数据处理,比如删除一些测试数据等,这里我们没有做任何工作。
public void beginAddUser(WebRequest theRequest) { theRequest.addParameter("name", "nameValue"); theRequest.addParameter("pass","passValue") ; theRequest.addParameter("tel","telValue") ; theRequest.addParameter("address","addressValue") ; theRequest.addParameter("email","emailValue"); } public void testAddUser() throws javax.ejb.EJBException{ servlet.addUser(request,response) ; }
在Cactus中,你需要用testXXX来命名你的方法,这样Cactus会自动调用这个方法进行测。而BeingXXX则是在调 用test方法之前调用,也就是说在一个功能测试之前运行。这里我们现在beginAddUser中添加一些必要的参数 。WebRequest是Cactus提供的一个类,它允许你设置一些Http参数,如果你使用了 theRequest.addParameter("name","nameValue"),那么在servlet中你就可以用request.getParameter("name") 来取得name的值。当然还可以设置Cookie,Http Head参数。在testAddUser()方法中我们测试addUser方法,如 果测试有异常,则会产生EJBException,得到一个测试失败。
public void testFindByName() throws javax.ejb.EJBException { User u=servlet.findByName("nameValue") ; UserInfo ui=u.getUserInfo() ; this.assertEquals("email",ui.getEmail()) ; this.assertEquals("tel",ui.getTel()) ; this.assertEquals("nameValue",u.getName()) ; this.assertEquals("passValue",u.getPassword()) ; }
这个测试是测试根据用户名查找用户,之后你可以用assertEquals方法来测试返回的值是否正确。
public static void main(String[] theArgs) { junit.textui.TestRunner.main(new String[]{ ManaServletTest.class.getName()}); }
这里我们使用textui来运行我们的测试类,提供文本的测试信息,还有一个Swing的测试方法,一共一个界面, 但是没有什么太大的意义。
到此我们介绍了所有的主要方法。最后我们谈谈如何运行这个测试。
<?xml version="1.0" encoding="ISO-8859-1"?>FilterRedirector org.apache.cactus.server.FilterTestRedirector FilterRedirector /FilterRedirector ServletRedirector org.apache.cactus.server.ServletTestRedirector JspRedirector /jspRedirector.jsp ServletRedirector /ServletRedirector JspRedirector /JspRedirector
哈哈~~,终于完成了所有的工作,我们可以看看运行结果,"哦,不",居然出现了一个Error,那就是你的程序出现了问题,仔细看看吧,测试是不会骗你的 :) 。 以上代码在 win2000+JBOSS3.0+MySql MAX 3.24+Cactus1.3上运行成功。
关于作者
韩伟,任北京某公司系统分析员,主要从事j2ee发面的开发,对设计模式,Java,软件工程很感兴趣。您可以通过email:java_cn@21cn.com跟他取得联系。
</td></tr></table>