用Cactus来测试J2ee应用
关键字:Cactus来测试J2ee Junit是当前最流行的 测试框架 ,它能够让 开发 人员很方便的编写测试单元,可以使他们"放心"地开发。但是现在很多的应用都是基于j2ee的,代码都是在服务器端的容器里面运行,这个使测试带来了一些麻烦。对于普通的jsp,servlet用Ju
关键字:Cactus来测试J2ee
Junit是当前最流行的
测试框架,它能够让
开发人员很方便的编写测试单元,可以使他们"放心"地开发。但是现在很多的应用都是基于j2ee的,代码都是在服务器端的容器里面运行,这个使测试带来了一些麻烦。对于普通的jsp,servlet用Junit来测试好像已经不是那么方便,对于EJB来说,特别是2.0版本,很多接口都是Local Interface,没有办法进行分布式的测试。那么我们如何进行这些代码的测试呢?Apache为我们提供了一个强大的工具 Cactus!它是一套简单,易于使用的服务器端测试框架,可以使开发人员很轻松的
测试服务器端的程序,他们会说:"哦,就是这么简单"。Cactus是Junit的一个扩展,但是它又和Junit有一些不同。Cactus的测试分为三种不同的测试类别,Jsp
TestCase,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 部署文件描述
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
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
ANT>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 (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的测试方法,一共一个界面, 但是没有什么太大的意义。
到此我们介绍了所有的主要方法。最后我们谈谈如何运行这个测试。
首先下载Cactus。
把lib/下的jar文件加入到 web app的lib下。以及你客户端的classpath中,这是最保险的,虽然不是所有 的jar都用的着。
设置你的Cactus.找到cactus.properties 文件,把它加入到客户端的classpath中。
修改cactus.properties 文件,把http://localhost:8080/test 改成你相应的设置,test是你web应用的 名称。其他设置可以不变。
修改服务器端web应用的配置,在web.xml中加入:
FilterRedirector
org.apache.cactus.server.FilterTestRedirector
FilterRedirector
/FilterRedirector
ServletRedirector
org.apache.cactus.server.ServletTestRedirector
JspRedirector
/jspRedirector.jsp
ServletRedirector
/ServletRedirector
原文转自:http://www.ltesting.net