原創(chuàng)作者:水工一木(jiangtang@263.net)。轉(zhuǎn)載請(qǐng)聲明
1. Junit測(cè)試框架簡(jiǎn)介[3]
Junit本質(zhì)上是一套框架,即開(kāi)發(fā)者制定了一套條條框框,遵循這些條條框框的要求編寫(xiě)測(cè)試代碼,如繼承某個(gè)類(lèi),實(shí)現(xiàn)某個(gè)接口,就可以用Junit進(jìn)行自動(dòng)測(cè)試了。
Junit是以?xún)蓚€(gè)關(guān)鍵模式來(lái)設(shè)計(jì)的,命令模式(Command Pattern)及集成模式(Composite Pattern):
2 命令模式:利用Junit框架提供的TestCase定義一個(gè)子類(lèi),在這個(gè)子類(lèi)中可以定義任意數(shù)量的testXXX()測(cè)試方法,在測(cè)試方法中編寫(xiě)代碼檢測(cè)某個(gè)方法被調(diào)用后對(duì)象的狀態(tài)與預(yù)期的狀態(tài)是否一致。當(dāng)這個(gè)子類(lèi)包含多個(gè)testXXX()方法時(shí),可以使用setUp()及tesrDown()方法建立測(cè)試的初始化數(shù)據(jù)(因?yàn)榇蟛糠址椒ǖ臏y(cè)試數(shù)據(jù)是相同的),這兩個(gè)方法如同測(cè)試的基礎(chǔ)設(shè)施,因此叫做測(cè)試基礎(chǔ)(fixture)。
2 集成模式:利用TestSuite可以將一個(gè)TestCase子類(lèi)中所有testXXX()方法包含進(jìn)來(lái)一起運(yùn)行,而且它本身也可以被別的TestSuite所包含,從而形成一種層次關(guān)系。這樣,通過(guò)TestSuite就可以組織任意深度的組合測(cè)試。
2. Cactus測(cè)試框架簡(jiǎn)介[4]
cactus單元測(cè)試工具是對(duì)junit框架的擴(kuò)充,使junit的思想和便利同樣用于Browser/Server web應(yīng)用程序中的測(cè)試;同時(shí),利用cactus還可以測(cè)試EJB類(lèi)。實(shí)際上,cactus只是提供了測(cè)試客戶(hù)端在容器上的一個(gè)代理,它的工作原理如下圖所示:
下面分步驟解釋在cactus Testcase子類(lèi)里每一個(gè)testXXX()方法的具體執(zhí)行步驟:
1. JUnit 測(cè)試運(yùn)行器調(diào)用YYYTestCase.runTest()方法。這個(gè)方法尋找 beginXXX(WebRequest)方法,如果找到則執(zhí)行。 傳給beginXXX(WebRequest)方法的參數(shù)WebRequest 可用來(lái)設(shè)置 HTTP頭, HTTP 參數(shù),這些參數(shù)將被發(fā)送到第2步的 Redirector 代理。
2. YYYTestCase.runTest() 方法打開(kāi)連向Redirector 代理的HTTP 連接,beginXXX(WebRequest)方法設(shè)置的HTTP協(xié)議參數(shù)將被送到代理。
3. Redirector 代理在服務(wù)端作為YYYTestCase的代理(其實(shí)YYYTestCase將被實(shí)例化兩次,一次在客戶(hù)端被JUnit 測(cè)試運(yùn)行器實(shí)例化,一次在服務(wù)器端被代理實(shí)例化,客戶(hù)端實(shí)例執(zhí)行beginXXX() and endXXX()方法,服務(wù)端實(shí)例執(zhí)行Junit 測(cè)試用例的方法setup()、testXXX()and teardown())。
4. 執(zhí)行Junit 測(cè)試用例的方法setup(),testXXX(),and teardown();
5. testXXX()方法調(diào)用服務(wù)端代碼來(lái)進(jìn)行測(cè)試,使用assertEquals()方法對(duì)測(cè)試結(jié)果和預(yù)期結(jié)果進(jìn)行比較,如果兩者相符為測(cè)試成功,否則為測(cè)試失敗;(EJB的問(wèn)題將在這里解決,因?yàn)槭窃谌萜鳝h(huán)境中執(zhí)行,因此甚至可以訪(fǎng)問(wèn)EJB的本地接口。)
6. 如果測(cè)試失敗,Redirector 代理將捕獲testXXX()方法拋出的的異常;
7. Redirector 代理將異常信息返回給客戶(hù)端的JUnit 測(cè)試運(yùn)行器,JUnit 測(cè)試運(yùn)行器可以生成測(cè)試報(bào)告;
8. 如果沒(méi)有異常出現(xiàn), YYYTestCase.runTest()方法尋找endXXX(org.apache.cactus.WebResponse)和endXXX(com.meterware.httpunit.WebResponse) (后者用在和httpunit集成中) 方法,如果找到則執(zhí)行。endXXX方法中,我們可以檢查返回的HTTP 頭, Cookies 和output stream ,這個(gè)檢查可以借助于Junit的 assertEquals或者cactus提供的幫助類(lèi)。
3. Junit與cactus的安裝
Junit的安裝:
1. 在http://www.junit.org/index.htm下載最新版本的Junit程序包;
2. 將Junit程序包解壓至某一目錄下$JUNIT_HOME下,如c:\junit;
3. 將$JUNIT_HOME/junit.jar和$JUNIT_HOME加入到CLASSPATH中,加入后者是因?yàn)闇y(cè)試?yán)淘?/SPAN>$JUNIT_HOME/junit目錄下。
Cactus的安裝與配置:
1. 在http://jakarta.apache.org/cactus/downloads.html下載cactus包。
2. 按照下圖設(shè)置classpath。classpath的設(shè)置非常重要,因?yàn)?/SPAN>90%以上的cactus錯(cuò)誤可能都是來(lái)自classpath的錯(cuò)誤設(shè)置。[5]
3. 設(shè)置客戶(hù)端cactus.properties。cactus需要redirector 代理才能工作,所以除了把這些代理考到相應(yīng)的webapp的類(lèi)路徑(對(duì)于filter和servlet代理)或webapp路徑(對(duì)于jsp代理)外,我們還需要告訴客戶(hù)端測(cè)試實(shí)例到哪里去找這些代理。一般的配置如下:
cactus.contextURL=http://localhost:7001/epiccweb
cactus.servletRedirectorName=ServletRedirector
cactus.jspRedirectorName=JspRedirector
cactus.filterRedirectorName=FilterRedirector
4. 設(shè)置web.xml,加入以下代碼
<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>
5. 如何編寫(xiě)測(cè)試類(lèi)
目錄結(jié)構(gòu)及命名規(guī)則
在介紹如何編寫(xiě)測(cè)試類(lèi)之前,先介紹一下測(cè)試類(lèi)的存放位置。一般來(lái)說(shuō),測(cè)試類(lèi)與被測(cè)試的類(lèi)具有相同的包結(jié)構(gòu),在某一模塊中,測(cè)試類(lèi)和被測(cè)試類(lèi)分別存放在src/和test/目錄下,如右圖所示。
另外,統(tǒng)一規(guī)定測(cè)試類(lèi)名為被測(cè)試類(lèi)名前加test,測(cè)試方法名為被測(cè)試方法名前加test。
建立測(cè)試類(lèi)
在本項(xiàng)目中,單元測(cè)試將涉及到三方面類(lèi)的測(cè)試:普通Java類(lèi)、EJB、Servlet。針對(duì)這三種不同的類(lèi),這里給出不同的解決方式。
1. 普通Java類(lèi)的測(cè)試
以com.picc.epicc.util.pub包中的StrHandler類(lèi)為例,為測(cè)試decode方法,建立testStrHandler如下:
package com.picc.epicc.util.pub;
import junit.framework.*;
import com.picc.epicc.util.pub.*;
import java.util.*;
public class testStrHandler extends TestCase
{
public testStrHandler(String name) {
super(name);
}
protected void setUp() {
}
protected void tearDown() {
}
public void testDecode()
{
StrHandler strHandler = new StrHandler();
String testStr = "haha|hehe|heihei|hoho|";
Collection testList = new Vector();
strHandler.decode(testStr,testList);
//檢驗(yàn)
assertEquals(testList.size(),4);
Iterator it = testList.iterator();
String hahaStr = (String)it.next();
assertEquals(hahaStr,"haha");
String hohoStr = "" ;
while(it.hasNext())
hohoStr = (String)it.next();
assertEquals(hohoStr,"hoho");
}
}
2. EJB的測(cè)試
由于EJB的測(cè)試需要依賴(lài)EJB容器環(huán)境,因此稍微復(fù)雜。尤其對(duì)于實(shí)體Bean的測(cè)試,由于實(shí)體Bean只實(shí)現(xiàn)本地接口(在本項(xiàng)目中,有些普通JavaBean由于涉及到對(duì)實(shí)體Bean的調(diào)用,如DtoFactory、Action等,因此也需在容器內(nèi)測(cè)試),因此如果利用普通的客戶(hù)端測(cè)試,還需為實(shí)體Bean建立遠(yuǎn)程接口,這將是一件非常繁冗的工作。利用cactus在容器中運(yùn)行的特性,可以實(shí)現(xiàn)EJB的測(cè)試,實(shí)現(xiàn)如下:
以com.picc.epicc.util.bl.action. CodeAction為例,建立testCodeAction如下:
package com.picc.epicc.util.bl.action;
import junit.framework.*;
import org.apache.cactus.*;
import com.picc.epicc.util.bl.action.*;
import com.picc.epicc.util.dto.domain.*;
import java.util.*;
public class testCodeAction extends ServletTestCase
{
public testCodeAction(String name) {
super(name);
}
protected void setUp() {
}
protected void tearDown() {
}
public void testGetDataByPK() throws Exception
{
CodeAction codeAction = new CodeAction();
EbsDcompanyDto ebsDcompanyDto = (EbsDcompanyDto)codeAction.getDataByPK("Dcompany","11019398");
assertEquals(ebsDcompanyDto.getComtype(),"2");
}
public void testGetData() throws Exception
{
CodeAction codeAction = new CodeAction();
Collection ebsDcompanyDtos = codeAction.getData("Dcompany","comcode='11019398'");
Iterator it = ebsDcompanyDtos.iterator();
if(it.hasNext())
{
EbsDcompanyDto ebsDcompanyDto = (EbsDcompanyDto)it.next();
System.out.println("comtype"+ebsDcompanyDto.getComtype());
assertEquals(ebsDcompanyDto.getComtype(),"2");
}
}
}
3. Servlet的測(cè)試
Servlet測(cè)試與EJB測(cè)試方法類(lèi)似,這里不再贅述。需要提出的是:在cactus中,可以在beginXXX()方法中準(zhǔn)備Http Request數(shù)據(jù),如下所示:
public void beginAddUser(WebRequest theRequest)
{
theRequest.addParameter("name", "nameValue");
theRequest.addParameter("pass","passValue") ;
theRequest.addParameter("tel","telValue") ;
}
另外,由于對(duì)Servlet的測(cè)試是實(shí)例化Servlet,容器不會(huì)自動(dòng)調(diào)用其init()方法,因此需要在setup()方法中顯式調(diào)用Servlet的init()方法。
6. Junit與ANT的結(jié)合
通過(guò)與ANT工具的集成,將實(shí)現(xiàn)測(cè)試的自動(dòng)化, 下面分別闡述Junit工具與cactus工具與ANT的結(jié)合。
1. Junit與ANT的結(jié)合:
<target name="testPub" depends="cpTest">
<java fork="yes" classname="junit.textui.TestRunner" failonerror="true">
<arg value="com.picc.epicc.util.pub.testAll" />
<classpath>
<pathelement location="${env.APPLIB}/util.jar" />//被測(cè)試類(lèi)
<pathelement location="${env.SYSLIB}/junit.jar" />
<pathelement location="${env.TESTLIB}/testUtil.jar" />//測(cè)試類(lèi)
<pathelement path="" />
<pathelement path="${java.class.path}" />
</classpath>
</java>
</target>
或者:
<target name="testPub" depends="cpTest">
<junit printsummary="yes" fork="yes" haltonfailure="no" >
<classpath>
<pathelement location="${env.APPLIB}/util.jar" />
<pathelement location="${env.SYSLIB}/junit.jar" />
<pathelement location="${env.TESTLIB}/testUtil.jar" />
<pathelement path="" />
<pathelement path="${java.class.path}" />
</classpath>
<formatter type="xml"/>
<test name="com.picc.epicc.util.pub.testStrHandler" haltonfailure="no" outfile="testPub" todir="../test/report/xml" />
</junit>
<junitreport todir="../test/report/xml">
<fileset dir="../test/report/xml">
<include name="testPub.xml"/>
</fileset>
<report format="noframes" todir="../test/report/html"/>
</junitreport>
</target>
2. cactus與ANT的結(jié)合
<target name="testAction" depends="cpTest">
<java fork="yes" classname="junit.textui.TestRunner" failonerror="true">
<arg value="com.picc.epicc.util.bl.action.testCodeAction" />
<classpath>
<pathelement location="${env.SYSLIB}/junit.jar" />
<pathelement location="${env.SYSLIB}/cactus-1.5-beta1.jar" />
<pathelement location="${env.SYSLIB}/aspectjrt-1.0.6.jar" />
<pathelement location="${env.SYSLIB}/commons-httpclient-2.0-beta2.jar" />
<pathelement location="${env.SYSLIB}/commons-logging-1.0.3.jar" />
<pathelement location="${env.TESTLIB}/testUtil.jar" />
<pathelement location="${env.USERCONFIG}" />//cactus.properties的位置
<pathelement path="" />
<pathelement path="${java.class.path}" />
</classpath>
</java>
</target>