Cactus是一套簡單,易于使用的服務(wù)器端測試框架,可以使開發(fā)人員很輕松的測試服務(wù)器端的程序。Cactus是Junit的一個擴展,但是它又和Junit有一些不同。Cactus的測試分為三種不同的測試類別,JspTestCase,ServletTestCase,F(xiàn)ilterTestCase,而不是像Junit一種TestCase。Cactus的測試代碼有服務(wù)器端和客戶端兩個部分,他們協(xié)同工作。在測試服務(wù)器端的應(yīng)用時,Cactus和Junit相比有什么優(yōu)勢呢?
一般EJB或者servlet,jsp都是運行在服務(wù)器上,如果你使用junit測試的話,你的測試是在客戶端,這使的運行環(huán)境和測試環(huán)境處于不同的系統(tǒng)環(huán)境中,這個有時候會不同的測試結(jié)果。
EJB2.0中的Local interface ,不允許遠(yuǎn)程調(diào)用。用Junit不好測試,而Cactus的redirector位于服務(wù)器端,可以和EJB運行在一個容器中,這使得它可以直接訪問Local Interface。
在一個EJB的應(yīng)用中,一般都會有一些前端應(yīng)用來訪問EJB,例如:jsp,servlet,javabean。這意味著你需要一個測試框架來測試這些前端的組件。Cactus提供了所有這些組件的測試方法。
Cactus和ant很好的結(jié)合在一起,可以很容易的完成自動化測試,減少了很多工作量。
一、工作原理
1. JUnit Test Runner調(diào)用YYYTestCase.runTest(),這個方法尋找beginXXX(ServletTestRequest )
2. YYYTestCase.runTest()打開一個到Redirector Proxy的HTTP連接
3. Redirector Proxy進(jìn)行如下操作
創(chuàng)建Test class的實例
創(chuàng)建一些Server對象(HttpServletRequest、ServletConfig、ServletContext)的Cactus wrapper
如果需要,創(chuàng)建一個HTTP Session
4. Redirector Proxy通過Reflection,執(zhí)行Test類的setUP()、testXXX()、tearDown()
5. testXXX()調(diào)用Server side classes的方法,并通過JUnit的assert API來驗證測試結(jié)果
6. 如果測試失敗,testXXX()方法拋出例外,Redirector Proxy會捕獲例外
7. Redirector Proxy向客戶端返回例外的有關(guān)信息,JUnit會將這些信息打印出來
8. 如果沒有發(fā)生例外,YYYTestCase.runTest()尋找并執(zhí)行endXXX(HttpURLConnection),在這兒你可以使用JUnit asserts檢查返回的HTTP Header、servlet output stream
Servlet Redirector Proxy
客戶端打開2個HTTP連接,一個用于執(zhí)行測試并取回Servlet輸出流;另一個取回測試結(jié)果。測試結(jié)果被存儲在一個變量中,并被放置到ServletContext供第二個連接取回。
JSP Redirector Proxy
客戶端打開2個連接,一個用于連接JSP Redirector執(zhí)行test,取回JSP 輸出流;第二個連接Servlet Redirector取回test結(jié)果。測試結(jié)果同樣被存儲在一個變量中,并被放置到ServletContext供第二個連接取回。
二、配置
client端
cactus.properties 配置Server端轉(zhuǎn)向器的地址
cactus.servletRedirectorURL = http://localhost:8080/test/ServletRedirector/ (注意:結(jié)尾為“/”)
cactus.jspRedirectorURL = http://localhost:8080/test/JspRedirector/
cactus.filterRedirectorURL = http://localhost:8080/test/FilterRedirector/
log_client.properties 配置log4j
在cactus.jar中已包含了一個確省的log_client.properties、log_server.properties文件。如果要提供自己的log_client.properties文件,要把此文件放在classpath中,并且一定要放在cactus.jar之前。
server端
web.xml
為了與上面client端的cactus.properties相配合,需要在server端部署一個命名為test的應(yīng)用,并寫一個配置文件web.xml,例示如下:
<web-app>
<filter>
<filter-name>FilterRedirector</filter-name>
<filter-class>org.apache.cactus.server.FilterRedirector</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterRedirector</filter-name>
<url-pattern>/FilterRecirector/</url-pattern>
</filter-mapping>
...同樣配置ServletRedirector、JspRedirector的類及URL,特殊之處是JspRedirector的類配置為:
<servlet>
<servlet-name>JspRedirector</servlet-name>
<jsp-file>...(/someDir)/jspRedirector.jsp</jsp-file>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
</servlet>
注:如果你使用了JspRedirector(即繼承了JspTestCase),你必須把jspRedirector.jsp拷貝到一個目錄(web.xml中的someDir)。jspRedirector.jsp文件在sample/web/test中,作為proxy調(diào)用server端的unit tests,文件示例如下:
<%@page import="org.apache.cactus.server.*" session="false" %><%
JspImplicitObjects objects = new JspImplicitObjects();
objects.setHttpServletRequest(request);
objects.setHttpServletResponse(response);
objects.setServletConfig(config);
objects.setServletContext(application);
objects.setJspWriter(out);
objects.setPageContext(pageContext);
JspTestRedirector redirector = new JspTestRedirector();
redirector.doGet(objects);
%>
log_server.properties
在cactus.jar中,如果要定制,好把修改后的log_server.properties打包到cactus.jar中,覆蓋原來的文件。
三、如何編寫測試案例(TestCase)
寫一個Test Case的步驟:
1. Import
import org.apache.cactsu.*;
import junit.framework.*;
2. Extends a cactus TestCase
public class TestSampleServlet extends Servlet/Jsp/FilterTestCase {
3. 標(biāo)準(zhǔn)JUnit方法
1) constructor
TestSampleServlet (String theName){
super(theName);
} // theName是Test的Name
2) main(String[]) //啟動一個JUnit Test Runner
junit.ui.TestRunner.main(new String[]{TestSampleServlet.class.getName()});
3) suite() //靜態(tài)方法,返回值為Test
return new TestSuite(TestSampleServlet.class)
4. setUP(), tearDown()
5. testXXX()
一個實例:
public void testXXX(){
SampleServlet servlet = new SampleServlet();
session.setAttribute(“name”,”value”);
servlet.doSomething(request);
assertEquals(“something”,result);
assertEquals(“otherValue”,session.getAttribute(“otherName”));
}
6. beginXXX(WebRequest) //可選
在WebRequest中設(shè)置所有的HTTP相關(guān)參數(shù),主要有:
setMethod(String)
setAutomaticSession(boolean)
setURL(...)
addParameter(String,String)等。
然后,你可以在testXXX()方法中調(diào)用HttpServletRequest的getQueryString()、getHeader()等得到。
注意:這個方法在client端執(zhí)行,先與testXXX()方法;所以,不要企圖去訪問任何the class variables that represent API objects (their values are null)。endXXX()也相同。
endXXX(HttpURLConnection) //可選
對于ServletTestCase,提供了如下隱含對象:
request ?C org.apache.cactus.server.HttpServletRequestWrapper
response ?C javax.servlet.http.HttpServletResponse
config ?C org.apache.cactus.server.ServletConfigWrapper
包裝config有2個目的:
4) 提供附加的方法,HttpConfigWrapper比javax.servlet.ServletConfig多2個方法
setServletName(String)
setInitParameter(String,String) // 與在web.xml中設(shè)定有同樣的效果
5) 可以返回包裝的ServletContext,
session ?C javax.servlet.http.HttpSession
提示1:如果使用到任何繼承自javax.servlet.GenericServlet的方法(比如:log()、getServletConfig()等),那么你需要調(diào)用servlet的init(ServletConfig)方法來實例ServletConfig對象。舉例如下:
public void testXXX(){
MyServletToTest servlet = new MyServletToTest();
servlet.init(config);
…servlet.someMethodToTest();
}
對于JspTestCase,提供了如下隱含對象:
request ?C org.apache.cactus.server.HttpServletRequestWrapper
response ?C javax.servlet.http.HttpServletResponse
config ?C org.apache.cactus.server.ServletConfigWrapper
包裝config有2個目的:
6) 提供附加的方法,HttpConfigWrapper比javax.servlet.ServletConfig多2個方法
setServletName(String)
setInitParameter(String,String) // 與在web.xml中設(shè)定有同樣的效果
7) 可以返回包裝的ServletContext,
session ?C javax.servlet.http.HttpSession
提示1:如果使用到任何繼承自javax.servlet.GenericServlet的方法(比如:log()、getServletConfig()等),那么你需要調(diào)用servlet的init(ServletConfig)方法來實例ServletConfig對象。舉例如下:
public void testXXX(){
MyServletToTest servlet = new MyServletToTest();
servlet.init(config);
…servlet.someMethodToTest();
}