Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1676087
  • 博文数量: 585
  • 博客积分: 14610
  • 博客等级: 上将
  • 技术积分: 7402
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-15 10:52
文章存档

2013年(5)

2012年(214)

2011年(56)

2010年(66)

2009年(44)

2008年(200)

分类:

2010-11-29 02:03:41

用Cactus来测试J2ee应用 


    
Java 专区中还有: 

教学 
工具与产品 
代码与组件 
所有文章 
实用技巧 




韩伟 (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来做测试呢?主要有一下几个理由: 

EJB2.0中的Local interface ,不允讯远程调用。用Junit不好测试,而Cactus的redirector位于服务器端,可以和EJB运行在一个容器中,这使得它可以直接访问Local Interface。 
一般EJB或者servlet,jsp都是运行在服务器上,如果你使用junit测试的话,你的测试是在客户端,这使的运行环境和测试环境处于不同的系统环境中,这个有时候会不同的测试结果。 
在一个EJB的应用中,一般都会有一些前端应用来访问EJB,例如:jsp,servlet,javabean。这就意味着你需要一个测试框架来测试这些前端的组件。Cactus提供了所有这些组件的测试方法。哦,太棒了。 
Cactus和ant很好的结合在一起,可以很容易的完成自动化测试,减少了很多工作量。当然,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 部署文件描述 

 

""> 
 
     
         
            UserManager 
            UserManager 
            usersystem.UserManagerLocalHome 
            usersystem.UserManagerLocal 
            usersystem.UserManagerBean 
            Stateless 
            Container 
             
                 
                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 
            
 
             
                email 
            
 
             
                address 
            
 
             
                tel 
            
 
            name 
        
 
    
 
     
         
            userInfo-user 
             
                userInfo 
                UserInfoRelationshipRole 
                One 
                 
                 
                    userInfo 
                    UserInfo 
                
 
                 
                    user 
                    user 
                
 
            
 
             
                user 
                UserRelationshipRole 
                One 
                 
                    user 
                    User 
                
 
                 
                    userInfo 
                    userInfo 
                
 
            
 
        
 
    
 
     
         
             
                User 
                * 
            
 
            Required 
        
 
         
             
                UserManager 
                * 
            
 
            Required 
        
 
         
             
                UserInfo 
                * 
            
 
            Required 
        
 
    
 
 

接下来是访问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 Coffee 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 Coffee 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的测试方法,一共一个界面, 但是没有什么太大的意义。 

到此我们介绍了所有的主要方法。最后我们谈谈如何运行这个测试。 

首先下载Cactus。 
把lib/下的jar文件加入到 web app的lib下。以及你客户端的classpath中,这是最保险的,虽然不是所有 的jar都用的着。 
设置你的Cactus.找到cactus.properties 文件,把它加入到客户端的classpath中。 
修改cactus.properties 文件,把 改成你相应的设置,test是你web应用的 名称。其他设置可以不变。 
修改服务器端web应用的配置,在web.xml中加入: 
 
     
        FilterRedirector 
        org.apache.cactus.server.FilterTestRedirector 
    
 

     
        FilterRedirector 
        /FilterRedirector 
    
 

     
        ServletRedirector 
        org.apache.cactus.server.ServletTestRedirector 
    
 

     
        JspRedirector 
        /jspRedirector.jsp 
    
 

     
        ServletRedirector 
        /ServletRedirector 
    
 

     
        JspRedirector 
        /JspRedirector 
    
 

编译ejb和servlet,把EJB文件的jar,和servlet的war文件打包成 ear文件。 
发布你的ear文件到web application. 
运行本地的测试文件ManaServletTest.class 
哈哈~~,终于完成了所有的工作,我们可以看看运行结果,"哦,不",居然出现了一个Error,那就是你的程序出现了问题,仔细看看吧,测试是不会骗你的 :) 。 以上代码在 win2000+JBOSS3.0+MySql MAX 3.24+Cactus1.3上运行成功。 

关于作者 

韩伟,任北京某公司系统分析员,主要从事j2ee发面的开发,对设计模式,Java,软件工程很感兴趣。您可以通过email:java_cn@21cn.com跟他取得联系。 
阅读(735) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~