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

軟件測試的時代 - 軟件測試思想、軟件測試技術新體驗!
             
J2EE開發平臺的軟件測試技術

                    來自:cybercorlin.net 作者:不詳
  前言─以測試為導向的軟件開發流程
   軟件開發流程的新興觀念是將軟件測試的角色,提升為系統開發時每一個階段都必須要持續且反復進行的重要任務,確保每一個階段都能及早發現潛藏于系統內的危險因子。當某一個階段的測試結果無法達到預期的要求時,就必須回溯到之前的開發階段,再次分析和審核,這種過程稱之為重構(Refactoring)。配合重構的機制,讓系統的品質都能夠在嚴密的測試監控下持續成長。不過由于網絡時代的革命興起之后,軟件系統的架構變得更為復雜,相對的軟件測試的發展也更顯得重要。
J2EE平臺內的軟件測試
   XP只定義了兩種測試的層級,第一種層級是「單元測試」,因為單元測試的用意是為了檢驗程序代碼是否合乎邏輯,而且是針對系統內部的模塊來測試,因此又可以稱做為程序邏輯測試(Code Logic Testing)。為了因應不同的應用程序開發平臺的特殊架構,在J2EE的平臺里,還發展出與J2EE Container 緊密結合的整合測試(Integration Testing)。
   另一種層級是接受度測試(Acceptance Testing),又稱作功能測試(Functional Testing)。在軟件測試中還有一個大家常聽到的是效能測試(Performance Testing)。由于效能測試與客戶的需求是密不可分,所以將它歸類為接受度測試的延伸應用。最后歸納起來,一個J2EE平臺可能所需要的測試流程,以及測試之間的關系,如圖一所示。

  在Web層內軟件測試的概念與流程,如圖二所示,其中鍵頭旁的數字符號代表著整個測試流程的執行步驟。首先先準備好受測數據與受測系統之后,借著虛擬瀏覽器來發出request,向受測系統取得包裹著HTML code的response。然后再利用測試平臺來協助我們進行受測數據與預期值的比對工作。當比對后所回報的結果都是正確無誤時,代表著受測系統的功能可以正常運作了。圖中的測試平臺與虛擬瀏覽器在測試中扮演著關鍵的角色。

  雖然發展測試平臺的概念已經行之有年了,然而具備有可延伸且開放式架構的測試平臺并不多,其中OpenSource社群以Java開發出來的JUnit,是極具代表性的測試平臺。
1 單元測試平臺─ JUnit
   JUnit平臺的設計架構是采用了命令(Command)和復合(Composite)兩種設計模式(Design Pattern)做為關鍵的組成架構。在JUnit平臺中的核心類別是TestCase,而每一個TestCase代表著一個命令對象。TestCase包含數個test method,用來測試被測類別內public method的產出對象與預期的結果是否相同。在JUnit平臺內有提供數種用來協助比對的assert method。
   JUnit平臺里還有另一個核心類別是TestSuite,而每一個TestSuite代表著一個復合的對象。一個TestSuite可以由數個TestCase或是數個TestSuite組成,因此可以根據測試的需求,拼湊出多個的TestSuite。整個JUnit測試平臺的組成架構,如圖三所示。在了解了JUnit平臺的架構之后,我們便可以運用JUnit平臺來發展受測系統的整合測試與功能測試。

代碼實例:
import junit.framework.*;
import java.util.Vector;
public class VectorTest extends TestCase {
protected Vector fEmpty;
protected Vector fFull;
public VectorTest(String name) {
super(name);
}
public static void main (String[] args) {
junit.textui.TestRunner.run (suite());
}
protected void setUp() {
fEmpty= new Vector();
fFull= new Vector();
fFull.addElement(new Integer(1));
fFull.addElement(new Integer(2));
fFull.addElement(new Integer(3));
}
public static Test suite() {
return new TestSuite(VectorTest.class);
}
public void testCapacity() {
int size= fFull.size();
for (int i= 0; i < 100; i++)
fFull.addElement(new Integer(i));
assertTrue(fFull.size() == 100+size);
}
public void testClone() {
Vector clone= (Vector)fFull.clone();
assertTrue(clone.size() == fFull.size());
assertTrue(clone.contains(new Integer(1)));
}
public void testContains() {
assertTrue(fFull.contains(new Integer(1)));
assertTrue(!fEmpty.contains(new Integer(1)));
}
public void testElementAt() {
Integer i= (Integer)fFull.elementAt(0);
assertTrue(i.intValue() == 1);
try {
Integer j= (Integer)fFull.elementAt(fFull.size());
} catch (ArrayIndexOutOfBoundsException e) {
return;
}
fail("Should raise an ArrayIndexOutOfBoundsException");
}
public void testRemoveAll() {
fFull.removeAllElements();
fEmpty.removeAllElements();
assertTrue(fFull.isEmpty());
assertTrue(fEmpty.isEmpty());
}
public void testRemoveElement() {
fFull.removeElement(new Integer(3));
assertTrue(!fFull.contains(new Integer(3)) );
}
}
import junit.framework.*;
import junit.runner.BaseTestRunner;
public class AllTests {
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static Test suite() {
TestSuite suite= new TestSuite("Framework Tests");
suite.addTestSuite(ExtensionTest.class);
suite.addTestSuite(TestCaseTest.class);
suite.addTest(SuiteTest.suite()); suite.addTestSuite(ExceptionTestCaseTest.class);
suite.addTestSuite(TestListenerTest.class);
suite.addTestSuite(ActiveTestTest.class);
suite.addTestSuite(AssertTest.class);
suite.addTestSuite(StackFilterTest.class);
suite.addTestSuite(SorterTest.class);
suite.addTestSuite(RepeatedTestTest.class);
suite.addTestSuite(TestImplementorTest.class);
if (!BaseTestRunner.inVAJava()) {
suite.addTestSuite(TextRunnerTest.class);
if (!isJDK11())
suite.addTest(new TestSuite(TestCaseClassLoaderTest.class));
}
return suite;
}
static boolean isJDK11() {
String version= System.getProperty("java.version");
return version.startsWith("1.1");
}
}
2 整合測試的觀念與Cactus應用
   整合測試提供了J2EE Container的環境,可以快速輕易地檢驗出Domain Object與J2EE Container的互動行為是否合乎邏輯。因此整合測試的對象是以一個EJB、Servlet或是JSP的程序代碼為基本單元。Open Source社群的Jakarta計劃中的子計劃Cactus,即是為了實作整合測試用的平臺而誕生的。
   Cactus基本上也是延伸JUnit平臺而發展出來的,因此它除了原有基本的method之外,還提供了可以用來模擬瀏覽器的內部行為的beingxxx( )和endxxx( )的method。這兩個method來 這些method的執行順序和與Web Container互動的行為模式,如圖四所示。

  我們利用beginxxx( )來設定要傳遞給受測對象的字符串參數。執行完beginxxx( )后,會發出request將參數名稱與參數值傳遞到Web Container。TestCase會執行setUp( ),將受測對象所需要的對象環境建立起來,接著在testxxx( )執行存取受測對象的動作。當存取受測對象的動作執行完后,便可以檢驗受測對象可能存放在session的產出物。然后在Web Container會執行釋放資源的動作,然后將response回傳到Client端。最后在Client端執行endxxx( )來進行比對HTML code是否和預期值相同,執行完endxxx()時也代表一個整合測試的結束。將這五個method所執行的功能匯整如表一所示。

  雖然Cactus架構提供了受測對象產出物與預期結果的比對功能,但是當回傳的HTML code的內容過于龐大復雜時,反而不利于比對的工作。因此采用了一個實用性的做法。此做法是在JSP或servlet欲產出的HTML code的程序代碼里,于關鍵的卷標內添加ID這種屬性。當endxxx( )要進行比對前,先讀取記載著ID屬性值與預期值的外部數據文件,再透過DOM的存取機制來取得HTML code,便能夠快速地比對關鍵的數據。不僅可以將比對的工作模塊化,更能夠在不需要重新編譯測試碼的情形下,隨時變更預期值。讀者們若有遇到相似的問題時,不妨可以采用與相同的策略來解決。
   整合測試不同于單元測試,雖然減低了撰寫測試碼的困難度,但也因為Domain Object與J2EE Container的結合,而不能為Domain Object提供單純的測試環境。因此若有其它的測試可以單純地檢驗整個系統,便可以彌補整合測試的不足。功能測試即是扮演這樣的一個角色。
配置信息與代碼實例:
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>
org.apache.cactus.server.ServletTestRedirector
</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1 used for testing</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ServletTestRunner</servlet-name>
<servlet-class>
org.apache.cactus.server.runner.ServletTestRunner
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletTestRunner</servlet-name>
<url-pattern>/ServletTestRunner</url-pattern>
</servlet-mapping>
實例testcase:
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
public class TestSampleServlet extends ServletTestCase
{
public TestSampleServlet(String theName)
{
super(theName);
}
public static Test suite()
{
return new TestSuite(TestSampleServlet.class);
}
public void beginSaveToSessionOK(WebRequest webRequest)
{
webRequest.addParameter("testparam", "it works!");
}
public void testSaveToSessionOK()
{
SampleServlet servlet = new SampleServlet();
servlet.saveToSession(request);
assertEquals("it works!", session.getAttribute("testAttribute"));
}
}
3 業務邏輯測試與StrutsTestcase應用
struts的使用越來越廣泛,但是并沒有合適測試框架與之對應.cactus, httpunit雖然都可以測試jsp,servlet,但基于struts框架的應用程序的測試依然比較麻煩。
不過,StrutsTestCase的出現多少解決了些問題,下面主要講講StrutsTestCase的應用。和其他的JSP/SERVLET測試框架一樣,StrutsTestCase也有兩種測試結構,一種是Mock結構,另一種是利用Cactus的結構。
因為StrutsTestCase也是基于junit上開發的,所以它的使用方法也類似于junit.可以參考他的例子和api.
可惜的是StrutsTestCase例子里沒有使用ant,因此它的運行較讓人頭痛,特別是有關配置文件的處理。StrutsTestCase在運行時必須能夠找到struts-config.xml和web.xml文件,默認的位置是web-inf/目錄下,所以web-inf目錄要存在于classpath或者在運行的時候指定。例如:我的位于d:/struts-test/下,struts-config.xml和web.xml位于d:/struts-test/webapp/WEB-INF/下,那么在運行測試的時候應當把d:/struts-test/webapp路徑放到classpath里。如果我的struts-config.xml不在/web-inf目錄里,而是在/web-inf/conf/目錄下,那么就須調用setConfigFile(String path)方法,按照剛才的情況,

public void testSuccessfulLogin() {
setRequestPathInfo("/login"); addRequestParameter("username","deryl"); addRequestParameter("password","radar");
actionPerform();
verifyForward("success"); }
public void testFailedLogin() {
addRequestParameter("username","deryl");
addRequestParameter("password","express");
setRequestPathInfo("/login");
actionPerform();
verifyForward("login");
}
public void setUp() throws Exception {
super.setUp() ;
setConfigFile("/WEB-INF/conf/struts-config.xml") ;
}
4 功能測試的觀念與HttpUnit應用
   以UML的術語來說,功能測試的對象是檢驗Use Case所規范的行為,測試系統是否符合所需要的功能,是否能達到使用者的需求?而單元測試的對象是檢驗對象Classes Diagram與Sequence Diagram所描述的關系與行為,測試單元是否執行正確,是否符合程序邏輯?。每當完成一個階段性的功能測試,也代表著完成了一部分的系統實作。
   Open Source社群的HttpUnit API套件,即是為了功能測試而發展出來的。HttpUnit是以Java撰寫出來的虛擬瀏覽器,用來模擬瀏覽器的內部行為。前一節所提到的Cactus檢驗HTML code的機制,也是采用HttpUnit來完成的。
    除此之外,HttpUnit還可以結合JUnit平臺撰寫測試碼來檢驗回傳的網頁內容是否與預期結果相符合。HttpUnit平臺的運作機制是建構在Http標準通訊協議之下,藉由模擬使用者瀏覽網站時,所發出的以對象的形式封裝的request訊號,將其送至到目的網站,然后等到該網站處理完此request之后,便將同樣以對象形式封裝的response訊號回傳給HttpUnit。
   由于HttpUnit所接收的是標準HTTP協議的response對象,因此不論該網站是靜態網頁語言或是用任何的動態服務器端語言寫成的,都可以透過HttpUnit來模擬網站瀏覽的行為并且取得標準的HTML code。
   市面上也有提供功能測試用的預錄播放軟件,可以事先錄下網站瀏覽的步驟,然后反復地播放預錄好的流程,最后回報測試的數據給測試人員,供測試人員進行分析,大大節省撰寫測試碼的負擔。然而此類軟件有以下的缺點:
1. 當網站設計的復雜度越高,瀏覽的分支流程越多,預錄好的流程便無法作有效的模塊化管理。
2. 預錄播放軟件雖然有提供記錄瀏覽步驟的script或是XML文件,雖然這些指令碼可以重復利用,然而若無法提供有效的偵錯機制,一旦安插了錯誤的程序,反而容易造成無法預期的錯誤產生。
3. 測試人員需要重新學習專屬于預錄播放軟件的script語言或是XML文件語法,無法從既有熟悉的程序語言來編寫瀏覽網站的程序。
4. 當網站的操作接口時常為了需求而新增或是修改原有的互動設計時,必須重新錄制新的瀏覽網站的程序,而無法重復利用。
   HttpUnit解決了軟件開發人員以上的困擾。HttpUnit是一種黑箱作業形式的測試工具,因此我們只要專注如何在JUnit平臺上撰寫模擬瀏覽器行為的測試碼即可。HttpUnit內的method執行順序和與Web Container互動的行為模式,如圖五所示,箭頭符號旁所標示的數字,代表著這些method的執行順序。

當我們在setup( )設定好受測的網址與相關的環境后,setUp( )會執行向受測網址進行存取的動作。當存取動作完成時,會將response回傳至Client端。此時可以在testxxx( )做HTML code和預期值比對的工作。最后在Client端執行釋放資源的動作,執行完tearDown()時也代表一個功能測試的完成。將這三個method所執行的功能匯整如表二所示。
雖然HttpUnit提供強大的仿真功能,但是HttpUnit本身還是存在兩個缺點。第一,當HttpUnit結合JUnit平臺做測試時,由于HttpUnit存取HTML code的方式與HTML內部的文件結構的關聯過于緊密,因此當網頁版面需要變動時,也需要修改相對應的測試碼。對于這樣的困擾,采用了與Cactus檢測HTML code同樣的改良策略,來達到快速比對而不用調整測試碼的好處。
   第二,在HttpUnit與JUnit平臺結合做測試的情況時,由于JUnit特殊的運作機制,無法記住每一個已經瀏覽過的網址狀態,因此當某個受測網址與其它網址的依存性強時,若要回傳正確的瀏覽狀態時,必須要用遞歸記憶的方式來達成。例如要存取第二個網頁必須記住第一個網頁的狀態,存取第三個網頁要記住第一個和第二個網頁的狀態,同理存取第n個網頁時需要記住第一個網頁到n-1個網頁,這樣的做法不易將測試碼模塊化,如圖六所示。

 于是利用了HttpUnit本身也可以寫成獨立運作的程序代碼的特性,寫成一個瀏覽網站步驟的仿真器。然后利用JUnit的setUp( )來存取受測網站的瀏覽狀態,便可以在testxxx( )取得正確的網頁狀態來進行比對的工作,經過模塊化后的HttpUnit測試架構如圖七所示。

5 可以參考的測試項目:
Junit 包中有比較簡單的測試用例的例子。
Eclipse 開發平臺的 JUnit Plugin Tests and Automated Testing Framework 插件中的JunitTest項目例子,比較詳細的介紹了junit測試平臺。
參考文獻
1. JUnit官方網站http://www.junit.org/index.htm
2. Cactus官方網站http://jakarta.apache.org/cactus/index.html
3. Strutstestcase網站http://strutstestcase.sourceforge.net/
4. HttpUnit 網站 http://sourceforge.net/projects/httpunit/
5. Eclipse 網站 http://www.eclipse.org/downloads/index.php


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