原創(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.   Junitcactus的安裝

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è)置classpathclasspath的設(shè)置非常重要,因?yàn)?/SPAN>90%以上的cactus錯(cuò)誤可能都是來(lái)自classpath的錯(cuò)誤設(shè)置。[5]

3.  設(shè)置客戶(hù)端cactus.propertiescactus需要redirector 代理才能工作,所以除了把這些代理考到相應(yīng)的webapp的類(lèi)路徑(對(duì)于filterservlet代理)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)、EJBServlet。針對(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)用,如DtoFactoryAction等,因此也需在容器內(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)用Servletinit()方法。

6. JunitANT的結(jié)合

通過(guò)與ANT工具的集成,將實(shí)現(xiàn)測(cè)試的自動(dòng)化, 下面分別闡述Junit工具與cactus工具與ANT的結(jié)合。

1.         JunitANT的結(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.         cactusANT的結(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>