測試時代首頁 | 測試時代論壇 | 測試交流會 | Blog社區 | 測試時代工作室 | 測試時代刊物 | 軟件測試資料

軟件測試的時代 - 軟件測試思想、軟件測試技術新體驗!
             
用Cactus來測試J2ee應用

                    作者:韓偉 (java_cn@21cn.com) 選自:IBM
  unit是當前最流行的測試框架,它能夠讓開發人員很方便的編寫測試單元,可以使他們"放心"地開發。但是現在很多的應用都是基于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 部署文件描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"

"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<display-name>UserManager</display-name>
<ejb-name>UserManager</ejb-name>
<local-home>usersystem.UserManagerLocalHome</local-home>
<local>usersystem.UserManagerLocal</local>
<ejb-class>usersystem.UserManagerBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-local-ref>
<description />
<ejb-ref-name>User</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>usersystem.UserHome</local-home>
<local>usersystem.User</local>
<ejb-link>User</ejb-link>
</ejb-local-ref>
<ejb-local-ref>
<description />
<ejb-ref-name>UserInfo</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>usersystem.UserInfoHome</local-home>
<local>usersystem.UserInfo</local>
<ejb-link>UserInfo</ejb-link>
</ejb-local-ref>
</session>
<entity>
<display-name>User</display-name>
<ejb-name>User</ejb-name>
<local-home>usersystem.UserHome</local-home>
<local>usersystem.User</local>
<ejb-class>usersystem.UserBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>User</abstract-schema-name>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
<cmp-field>
<field-name>password</field-name>
</cmp-field>
<primkey-field>name</primkey-field>
<query>
<query-method>
<method-name>findAll</method-name>
<method-params />
</query-method>
<ejb-ql>select Object(theUser) from User as theUser</ejb-ql>
</query>
</entity>
<entity>
<display-name>UserInfo</display-name>
<ejb-name>UserInfo</ejb-name>
<local-home>usersystem.UserInfoHome</local-home>
<local>usersystem.UserInfo</local>
<ejb-class>usersystem.UserInfoBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>UserInfo</abstract-schema-name>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
<cmp-field>
<field-name>email</field-name>
</cmp-field>
<cmp-field>
<field-name>address</field-name>
</cmp-field>
<cmp-field>
<field-name>tel</field-name>
</cmp-field>
<primkey-field>name</primkey-field>
</entity>
</enterprise-beans>
<relationships>
<ejb-relation>
<ejb-relation-name>userInfo-user</ejb-relation-name>
<ejb-relationship-role>
<description>userInfo</description>
<ejb-relationship-role-name>UserInfoRelationshipRole</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<cascade-delete />
<relationship-role-source>
<description>userInfo</description>
<ejb-name>UserInfo</ejb-name>
</relationship-role-source>
<cmr-field>
<description>user</description>
<cmr-field-name>user</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<description>user</description>
<ejb-relationship-role-name>UserRelationshipRole</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<description>user</description>
<ejb-name>User</ejb-name>
</relationship-role-source>
<cmr-field>
<description>userInfo</description>
<cmr-field-name>userInfo</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
</relationships>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>User</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>UserManager</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>UserInfo</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

接下來是訪問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.*;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @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;

/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @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中加入:
<?xml version="1.0" encoding="ISO-8859-1"?>
<filter>
<filter-name>FilterRedirector</filter-name>
<filter-class>org.apache.cactus.server.FilterTestRedirector</filter-class>
</filter>

<filter-mapping>
<filter-name>FilterRedirector</filter-name>
<url-pattern>/FilterRedirector</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
</servlet>

<servlet>
<servlet-name>JspRedirector</servlet-name>
<jsp-file>/jspRedirector.jsp</jsp-file>
</servlet>

<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>JspRedirector</servlet-name>
<url-pattern>/JspRedirector</url-pattern>
</servlet-mapping>

編譯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跟他取得聯系。


測試時代首頁 | 測試時代論壇 | 測試交流會 | Blog社區 | 測試時代工作室 | 測試時代刊物 | 軟件測試資料
 
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97