• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    JUnit實戰

    發布: 2007-6-22 07:38 | 作者:   | 來源:   | 查看: 47次 | 進入軟件測試論壇討論

    領測軟件測試網

       

    前言

    由于現在公司進行Unit Test Case的整理階段,所以抽空對Junit進行了一下了解,以下是集合了眾家所長之精華(考慮的是按我的思路總結的,也許不能完全表達作者的思路,所以在附錄中有所有我參考的文章地址,大家不妨去看看原文)。

    一、測試的概念

    長期以來,我所接觸的軟件開發人員很少有人能在開發的過程中進行測試工作。大部分的項目都是在最終驗收的時候編寫測試文檔,有些項目甚至沒有測試文檔,F在情況有了改變。我們一直提倡UML、RUP、軟件工程、CMM,目的只有一個,提高軟件編寫的質量。舉一個極端的例子:如果你是一個超級程序設計師,一個傳奇般的人物(你可以一邊喝咖啡,一邊聽著音樂,同時編寫這操作系統中關于進程調度的模塊,而且兩天時間內就完成了。。我真得承認,有這樣的人(那個編寫UNIX中的vi編輯器的家伙就是這種人)。然而非常遺憾的是這些神仙們并沒有留下任何關于如何修成正果的README,所以我們這些凡人--在同一時間只能將注意力集中到若干點(據科學統計,我并不太相信,一般的人只能同時考慮最多7個左右的問題,高手可以達到12個左右),而不能既縱覽全局又了解細節--只能期望于其他的方式來保證我們所編寫的軟件質量。

    為了說明我們這些凡人是如何的笨。有一個聰明人提出了軟件熵(software entropy)的概念:一個程序從設計很好的狀態開始,隨著新的功能不斷地加入,程序逐漸地失去了原有的結構,最終變成了一團亂麻。你可能會爭辯,在這個例子中,設計很好的狀態實際上并不好,如果好的話,就不會發生你所說的情況。是的,看來你變聰明了,可惜你還應該注意到兩個問題:1)我們不能指望在恐龍紀元(大概是十年前)設計的結構到了現在也能適用吧;2)擁有簽字權的客戶代表可不理會加入一個新功能是否會對軟件的結構有什么影響,即便有影響也是程序設計人員需要考慮的問題。如果你拒絕加入這個你認為致命的新功能,那么你很可能就失去了你的住房貸款和面包(對中國工程師來說也許是米飯或面條,要看你是南方人還是北方人)。

    另外,需要說明的是我看過的一些講解測試的書都沒有我寫的這么有人情味(不好意思...)。我希望看到這篇文章的兄弟姐妹能很容易地接受測試的概念,并付諸實施。所以有些地方寫的有些夸張,歡迎對測試有深入理解的兄弟姐妹能體察民情,并不吝賜教。

    好了,我們現在言歸正傳。要測試,就要明白測試的目的。我認為測試的目的很簡單也極具吸引力:寫出高質量的軟件并解決軟件熵這一問題。想象一下,如果你寫的軟件和Richard Stallman(GNU、FSF的頭兒)寫的一樣有水準的話,是不是很有成就感?如果你一直保持這種高水準,我保證你的薪水也會有所變動。

    測試也分類,白箱測試、黑箱測試、單元測試、集成測試、功能測試...。我們先不管有多少分類,如何分類。先看那些對我們有用的分類,關于其他的測試,有興趣的人可參閱其他資料。白箱測試是指在知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能的條件下所作的測試。一般是由開發人員完成。因為開發人員最了解自己編寫的軟件。本文也是以白箱測試為主。黑箱測試則是指在知道被測試的軟件完成什么樣(What)的功能的條件下所作的測試。一般是由測試人員完成。黑箱測試不是我們的重點。本文主要集中在單元測試上,單元測試是一種白箱測試。目的是驗證一個或若干個類是否按所設計的那樣正常工作。集成測試則是驗證所有的類是否能互相配合,協同完成特定的任務,目前我們暫不關心它。下面我所提到的測試,除非特別說明,一般都是指單元測試。

    需要強調的是:測試是一個持續的過程。也就是說測試貫穿與開發的整個過程中,單元測試尤其適合于迭代增量式(iterative and incremental)的開發過程。Martin Fowler(有點兒像引用孔夫子的話)甚至認為:“在你不知道如何測試代碼之前,就不應該編寫程序。而一旦你完成了程序,測試代碼也應該完成。除非測試成功,你不能認為你編寫出了可以工作的程序。”我并不指望所有的開發人員都能有如此高的覺悟,這種層次也不是一蹴而就的。但我們一旦了解測試的目的和好處,自然會堅持在開發過程中引入測試。因為我們是測試新手,我們也不理會那些復雜的測試原理,先說一說最簡單的:測試就是比較預期的結果是否與實際執行的結果一致。如果一致則通過,否則失敗?聪旅娴睦樱

    //將要被測試的類

    public class Car

    {

    public int getWheels()

    {

    return 4;

    }

    }

    //執行測試的類

    public class testCar

    {

    public static void main(String[] args)

    {

    testCar myTest = new testCar();

    myTest.testGetWheels();

    }

    public void testGetWheels ()

    {

    int expectedWheels = 5;

    Car myCar =new Car();

    if (expectedWheels==myCar.getWheels())

    System.out.println("test [Car]: getWheels works perfected!");

    else

    System.out.println("test [Car]: getWheels DOESN'T work!");

    }

    }

    如果你立即動手寫了上面的代碼,你會發現兩個問題:

    第一,如果你要執行測試的類testCar,你必須必須手工敲入如下命令:

    [Windows] D:\>java testCar

    [Unix] % java testCar

    即便測試如例示的那樣簡單,你也有可能不愿在每次測試的時候都敲入上面的命令,而希望在某個集成環境中(IDE)點擊一下鼠標就能執行測試。后面的章節會介紹到這些問題。

    第二,如果沒有一定的規范,測試類的編寫將會成為另一個需要定義的標準。沒有人希望查看別人是如何設計測試類的。如果每個人都有不同的設計測試類的方法,光維護被測試的類就夠煩了,誰還顧得上維護測試類?另外有一點我不想提,但是這個問題太明顯了,測試類的代碼多于被測試的類!這是否意味這雙倍的工作?不!

    1) 不論被測試類-Car 的 getWheels 方法如何復雜,測試類-testCar 的testGetWheels 方法只會保持一樣的代碼量。

    2)提高軟件的質量并解決軟件熵這一問題并不是沒有代價的。testCar就是代價。

    我們目前所能做的就是盡量降低所付出的代價:我們編寫的測試代碼要能被維護人員容易的讀取,我們編寫測試代碼要有一定的規范。最好IDE工具可以支持這些規范。好了,你所需要的就是JUnit。一個Open Source的項目。用其主頁上的話來說就是:“ JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個回歸測試框架(regression testing framework)。用于Java開發人員編寫單元測試之用。”所謂框架就是Erich Gamma 和 Kent Beck 定下了一些條條框框,你編寫的測試代碼必須遵循這個條條框框:繼承某個類,實現某個接口。其實也就是我們前面所說的規范。好在JUnit目前得到了大多數軟件工程師的認可。遵循JUnit我們會得到很多的支持。回歸測試就是你不斷地對所編寫的代碼進行測試:編寫一些,測試一些,調試一些,然后循環這一過程,你會不斷地重復先前的測試,哪怕你正編寫其他的類,由于軟件熵的存在,你可能在編寫第五個類的時候發現,第五個類的某個操作會導致第二個類的測試失敗。通過回歸測試我們抓住了這條大Bug。

    二、JUnit簡介及為什么要使用JUint

    JUnit就是對程序代碼進行單元測試的一種Java框架。通過每次修改程序之后測試代碼,程序員就可以保證代碼的的少量變動不會破壞整個系統。要不是有Junit這樣的自動化測試工具,代碼的的反復測試簡直會把人累死而且還可能不準確,F在好了,測試過程可以頻繁進行而且還是自動的,所以你可以令程序錯誤降低到最少。它寫的是單元測試(Unit Test):軟件工程里的白盒測試,就是測試某個類的某個方法的功能。XP 中推崇的 test first design 就是基于以上的技術。

    如果你要寫一段代碼:

    1. 先用 junit 寫測試,然后再寫代碼

    2. 寫完代碼,運行測試,測試失敗

    3. 修改代碼,運行測試,直到測試成功

    如果以后對程序進行修改,優化 ( refactoring ),只要再運行測試代碼,如果所有的測試都成功,則代碼修改完成。

    Java 下的 team 開發,一般采用 cvs(版本控制) + ant(項目管理) + junit(集成測試) 的模式:

    1. 每天早上上班,每個開發人員從 cvs server 獲取一個整個項目的工作拷貝;

    2. 拿到自己的任務,先用 junit 寫今天的任務的測試代碼;

    3. 然后寫今天任務的代碼,運行測試,直到測試通過,任務完成;

    4. 在下班前一兩個小時,各個開發人員把任務提交到 cvs server;

    5. 然后由主管對整個項目運行自動測試,哪個測試出錯,就找相關人員修改,直到所有測試通過。下班...

    先寫測試,再寫代碼的好處:

    從技術上強制你先考慮一個類的功能,也就是這個類提供給外部的接口,而不至于太早陷入它的細節。這是面向對象提倡的一種設計原則。好的測試其實就是一個好的文檔,這個類使用者往往可以通過查看這個類的測試代碼了解它的功能。特別的,如果你拿到別人的一個程序,對他寫測試是最好的了解這個程序的功能的方法。 xp的原則是 make it simple,不是很推薦另外寫文檔,因為項目在開發過程中往往處于變動中,如果在早期寫文檔,以后代碼變動后還得同步文檔,多了一個工作,而且由于項目時間緊往往文檔寫的不全或與代碼不一致,與其這樣,不如不寫。而如果在項目結束后再寫文檔,開發人員往往已經忘記當時寫代碼時的種種考慮,況且有下一個項目的壓力,管理人員也不愿意再為舊的項目寫文檔,導致以后維護的問題。沒有人能保證需求不變動,以往項目往往對需求的變動大為頭疼,害怕這個改動會帶來其他地方的錯誤。為此,除了設計好的結構以分割項目外(松耦合),但如果有了測試,并已經建立了一個好的測試框架,對于需求的變動,修改完代碼后,只要重新運行測試代碼,如果測試通過,也就保證了修改的成功,如果測試中出現錯誤,也會馬上發現錯在哪里,修改相應的部分,再運行測試,直至測試完全通過。

    軟件公司里往往存在開發部門和測試部門之間的矛盾:由于開發和測試分為兩個部門,多了一層溝通的成本和時間,溝通往往會產生錯誤的發生。而且極易形成一個怪圈:開發人員為了趕任務,寫了爛爛的代碼,就把它扔給測試人員,然后寫其他的任務,測試當然是失敗的,又把代碼拿回去重寫,而且在國內往往一個軟件公司技術最差的部門就是測試部門(好的人都跑去寫代碼了),測試就成了一個很頭疼的問題。這種怪圈的根源是責任不清,根據 xp 中的規定:寫這個代碼的人必須為自己的代碼寫測試,而且只有測試通過,才算完成這個任務(這里的測試包括所有的測試,如果測試時發現由于你的程序導致別的模塊的測試失敗,你有責任通知相關人員修改直至集成測試通過),這樣就可以避免這類問題的發生。

    三、安裝

    1. 獲取JUnit的軟件包,從Junit(http://www.junit.org/index.htm或http://download.sourceforge.net/junit/)下載最新的軟件包。這里我使用的是http://download.sourceforge.net/junit/junit2.zip。

    2. 將其在適當的目錄下解包(我安裝在D:\junit2)。這樣在安裝目錄(也就是你所選擇的解包的目錄)下你找到一個名為junit.jar的文件。將這個jar文件加入你的CLASSPATH系統變量。(IDE的設置會有所不同,參看你所喜愛的IDE的配置指南)JUnit就安裝完了。

    四、運行

    通過前面的介紹,我們對JUnit有了一個大概的輪廓。知道了它是干什么的,F在讓我們動手改寫上面的測試類testCar使其符合Junit的規范--能在JUnit中運行。

    //執行測試的類(JUnit版)

    import junit.framework.*;

    public class testCar extends TestCase

    {

    protected int expectedWheels;

    protected Car myCar;

    public testCar(String name)

    {

    super(name);

    }

    protected void setUp()

    {

    expectedWheels = 4;

    myCar = new Car();

    }

    public static Test suite()

    {

    /** the type safe way */

    /*

    TestSuite suite= new TestSuite();

    suite.addTest(

    new testCar("Car.getWheels")

    {

    protected void runTest()

    {

    testGetWheels();

    }

    }

    );

    return suite;

    */

    /** the dynamic way */

    return new TestSuite(testCar.class);

    }

    public void testGetWheels()

    {

    assertEquals(expectedWheels, myCar.getWheels());

    }

    }

    改版后的testCar已經面目全非。先讓我們了解這些改動都是什么含義,再看如何執行這個測試。

    1>import語句,引入JUnit的類。(沒問題吧)

    2>繼承 TestCase ?梢詴簳r將一個TestCase看作是對某個類進行測試的方法的集合。詳細介紹請參看JUnit資料

    3>setUp()設定了進行初始化的任務。我們以后會看到setUp會有特別的用處。

    4>testGetWheeels()對預期的值和myCar.getWheels()返回的值進行比較,并打印比較的結果。assertEquals是junit.framework.Assert中所定義的方法,junit.framework.TestCase繼承了junit.framework.Assert。

    5>suite()是一個很特殊的靜態方法。JUnit的TestRunner會調用suite方法來確定有多少個測試可以執行。上面的例子顯示了兩種方法:靜態的方法是構造一個內部類,并利用構造函數給該測試命名(test name, 如 Car.getWheels),其覆蓋的runTest()方法,指明了該測試需要執行那些方法--testGetWheels()。動態的方法是利用內。╮eflection)來實現runTest(),找出需要執行那些測試。此時測試的名字即是測試方法(test method,如testGetWheels)的名字。JUnit會自動找出并調用該類的測試方法。

    6>將TestSuite看作是包裹測試的一個容器。如果將測試比作葉子節點的話,TestSuite就是分支節點。實際上TestCase,TestSuite以及TestSuite組成了一個composite Pattern。JUnit的文檔中有一篇專門講解如何使用Pattern構造Junit框架。有興趣的朋友可以查看JUnit資料。

    如何運行該測試呢?手工的方法是鍵入如下命令:

    [Windows] D:\>java junit.textui.TestRunner testCar

    [Unix] % java junit.textui.TestRunner testCar

    別擔心你要敲的字符量,以后在IDE中,只要點幾下鼠標就成了。運行結果應該如下所示,表明執行了一個測試,并通過了測試:

    .

    Time: 0

    OK (1 tests)

    如果我們將Car.getWheels()中返回的的值修改為3,模擬出錯的情形,則會得到如下結果:

    .F

    Time: 0.16

    FAILURES!!!

    Test Results:

    Run: 1 Failures: 1 Errors: 0

    There was 1 failure:

    1) testCar.testGetWheels "expected:<3> but was:<4>"

    注意:Time上的小點表示測試個數,如果測試通過則顯示OK。否則在小點的后邊標上F,表示該測試失敗。注意,在模擬出錯的測試中,我們會得到詳細的測試報告“expected:<3> but was:<4>”,這足以告訴我們問題發生在何處。下面就是你調試,測試,調試,測試...的過程,直至得到期望的結果。

    五、Design by Contract(這句話我沒法翻譯)

    Design by Contract本是Bertrand Meyer(Eiffel語言的創始人)開發的一種設計技術。我發現在JUnit中使用Design by Contract會帶來意想不到的效果。Design by Contract的核心是斷言(assersion)。斷言是一個布爾語句,該語句不能為假,如果為假,則表明出現了一個bug。Design by Contract使用三種斷言:前置條件(pre-conditions)、后置條件(post-conditions)和不變式(invariants)這里不打算詳細討論Design by Contract的細節,而是希望其在測試中能發揮其作用。

    前置條件在執行測試之前可以用于判斷是否允許進入測試,即進入測試的條件。如 expectedWheels > 0, myCar != null。后置條件用于在測試執行后判斷測試的結果是否正確。如 expectedWheels==myCar.getWheels()。而不變式在判斷交易(Transaction)的一致性(consistency)方面尤為有用。我希望JUnit可以將Design by Contract作為未來版本的一個增強。

    六、Refactoring(這句話我依然沒法翻譯)

    Refactoring本來與測試沒有直接的聯系,而是與軟件熵有關,但既然我們說測試能解決軟件熵問題,我們也就必須說出解決之道。(僅僅進行測試只能發現軟件熵,Refactoring則可解決軟件熵帶來的問題。)軟件熵引出了一個問題:是否需要重新設計整個軟件的結構?理論上應該如此,但現實不允許我們這么做。這或者是由于時間的原因,或者是由于費用的原因。重新設計整個軟件的結構會給我們帶來短期的痛苦。而不停地給軟件打補丁甚至是補丁的補丁則會給我們帶來長期的痛苦。(不管怎樣,我們總處于水深火熱之中)

    Refactoring是一個術語,用于描述一種技術,利用這種技術我們可以免于重構整個軟件所帶來的短期痛苦。當你refactor時,你并不改變程序的功能,而是改變程序內部的結構,使其更易理解和使用。如:該變一個方法的名字,將一個成員變量從一個類移到另一個類,將兩個類似方法抽象到父類中。所作的每一個步都很小,然而1-2個小時的Refactoring工作可以使你的程序結構更適合目前的情況。Refactoring有一些規則:

    1> 不要在加入新功能的同時refactor已有的代碼。在這兩者間要有一個清晰的界限。如每天早上1-2個小時的Refactoring,其余時間添加新的功能;

    2> 在你開始Refactoring前,和Refactoring后都要保證測試能順利通過,否則Refactoring沒有任何意義;

    3> 進行小的Refactoring,大的就不是Refactoring了。如果你打算重構整個軟件,就沒有必要Refactoring了。只有在添加新功能和調試bug時才又必要Refactoring。不要等到交付軟件的最后關頭才Refactoring。那樣和打補丁的區別不大。Refactoring 用在回歸測試中也能顯示其威力。要明白,我不反對打補丁,但要記住打補丁是應該最后使用的必殺絕招。(打補丁也需要很高的技術,詳情參看微軟網站)

    七、IDE對JUnit的支持

    目前支持JUnit的Java IDE 包括

    IDE
    方式
    分數(1-5,滿分5)

    Forte for Java 3.0 Enterprise Edition
    plug-in
    3

    Jbuilder 9 Enterprise Edition
    integrated with IDE
    4

    Visual Age for Java
    support
    N/A

    在IDE中如何使用JUnit,是非常具體的事情。不同的IDE有不同的使用方法。一旦理解了JUnit的本質,使用起來就十分容易了。所以我們不依賴于具體的IDE,而是集中精力講述如何利用JUnit編寫單元測試代碼。心急的人可參看資料。

    八、小結

    你一旦安裝完JUnit,就有可能想試試我們的Car和testCar類,沒問題,我已經運行過了,你得到的結果應該和我列出的結果類似。接下來,你可能會先寫測試代碼,再寫工作代碼,或者相反,先寫工作代碼,再寫測試代碼。我更贊成使用前一種方法:先寫測試代碼,再寫工作代碼。因為這樣可以使我們編寫工作代碼時清晰地了解工作類的行為。

    要注意編寫一定能通過的測試代碼(如文中的例子)并沒有任何意義,只有測試代碼能幫助我們發現bug,測試代碼才有其價值。此外測試代碼還應該對工作代碼進行全面的測試。如給方法調用的參數傳入空值、錯誤值和正確的值,看看方法的行為是否如你所期望的那樣。

    你現在已經知道了編寫測試類的基本步驟:

    1> 擴展TestCase類;

    2> 覆蓋runTest()方法(可選);

    3> 寫一些testXXXXX()方法。

    Fixture

    接下來的問題是,如果你要對一個或若干個的類執行多個測試,該怎么辦?JUnit對此有特殊的解決辦法。如果需要在一個或若干個的類執行多個測試,這些類就成為了測試的context。在JUnit中被稱為Fixture(如testCar類中的 myCar 和 expectedWheels )。當你編寫測試代碼時,你會發現你花費了很多時間配置/初始化相關測試的Fixture。將配置Fixture的代碼放入測試類的構造方法中并不可取,因為我們要求執行多個測試,我并不希望某個測試的結果意外地(如果這是你要求的,那就另當別論了)影響其他測試的結果。通常若干個測試會使用相同的Fixture,而每個測試又各有自己需要改變的地方。為此,JUnit提供了兩個方法,定義在TestCase類中。

    protected void setUp() throws java.lang.Exception

    protected void tearDown() throws java.lang.Exception

    覆蓋setUp()方法,初始化所有測試的Fixture(你甚至可以在setUp中建立網絡連接),將每個測試略有不同的地方在testXXX()方法中進行配置。覆蓋tearDown()(我總想起一首叫雨滴的吉他曲),釋放你在setUp()中分配的永久性資源,如數據庫連接。當JUnit執行測試時,它在執行每個testXXXXX()方法前都調用setUp(),而在執行每個testXXXXX()方法后都調用tearDown()方法,由此保證了測試不會相互影響。

    TestCase

    需要提醒一下,在junit.framework.Assert類中定義了相當多的assert方法,主要有assert(),assertEquals(), assertNull(), assertSame(), assertTrue(), fail()等方法。如果你需要比較自己定義的類,如Car。assert方法需要你覆蓋Object類的equals()方法,以比較兩個對象的不同。實踐表明:如果你覆蓋了Object類的equals()方法,最好也覆蓋Object類的hashCode()方法。再進一步,連帶Object類的toString()方法也一并覆蓋。這樣可以使測試結果更具可讀性。

    當你設置好了Fixture后,下一步是編寫所需的testXXX()方法。一定要保證testXXX()方法的public屬性,否則無法通過內。╮eflection)對該測試進行調用。每個擴展的TestCase類(也就是你編寫的測試類)會有多個testXXX()方法。一個testXXX()方法就是一個測試。要想運行這個測試,你必須定義如何運行該測試。如果你有多個testXXX()方法,你就要定義多次。JUnit支持兩種運行單個測試的方法:靜態的和動態的方法。

    靜態的方法就是覆蓋TestCase類的runTest()方法,一般是采用內部類的方式創建一個測試實例:

    TestCase test01 = new testCar("test getWheels")

    {

    public void runTest()

    {

    testGetWheels();

    }

    }

    采用靜態的方法要注意要給每個測試一個名字(這個名字可以任意起,但你肯定希望這個名字有某種意義),這樣你就可以區分那個測試失敗了。

    動態的方法是用內。↖ntrospection,檢查標準管理構件接口和應用設計模式的過程)來實現runTest()以創建一個測試實例。這要求測試的名字就是需要調用的測試方法的名字:

    TestCase test01 = new testCar("testGetWheels");

    JUnit會動態查找并調用指定的測試方法。動態的方法很簡潔,但如果你鍵入了錯誤的名字就會得到一個令人奇怪的NoSuchMethodException異常。動態的方法和靜態的方法都很好,你可以按照自己的喜好來選擇。(先別著急選擇,后面還有一種更酷的方法等著你呢。)

    TestSuite

    一旦你創建了一些測試實例,下一步就是要讓他們能一起運行。我們必須定義一個TestSuite。在JUnit中,這就要求你在TestCase類中定義一個靜態的suite()方法。suite()方法就像main()方法一樣,JUnit用它來執行測試。在suite()方法中,你將測試實例加到一個TestSuite對象中,并返回這個TestSuite對象。一個TestSuite對象可以運行一組測試。TestSuite和TestCase都實現了Test接口(interface),而Test接口定義了運行測試所需的方法。這就允許你用TestCase和TestSuite的組合創建一個TestSuite。這就是為什么我們前面說TestCase,TestSuite以及TestSuite組成了一個composite Pattern的原因。例子如下:

    public static Test suite()

    {

    TestSuite suite= new TestSuite();

    suite.addTest(new testCar("testGetWheels"));

    suite.addTest(new testCar("testGetSeats"));

    return suite;

    }

    從JUnit 2.0開始,有一種更簡單的動態定義測試實例的方法。你只需將類傳遞給TestSuite,JUnit會根據測試方法名自動創建相應的測試實例。所以你的測試方法最好取名為testXXX()。例子如下:

    public static Test suite()

    {

    return new TestSuite(testCar.class);

    }

    從JUnit的設計我們可看出,JUnit不僅可用于單元測試,也可用于集成測試。關于如何用JUnit進行集成測試請參考相關資料。

    為了兼容性的考慮,下面列出使用靜態方法的例子:

    public static Test suite()

    {

    TestSuite suite= new TestSuite();

    suite.addTest(

    new testCar("Car.getWheels")

    {

    protected void runTest()

    {

    testGetWheels();

    }

    }

    );

    return suite;

    }

    TestRunner

    有了TestSuite我們就可以運行這些測試了,JUnit提供了三種界面來運行測試

    [Text UI] junit.textui.TestRunner

    [AWT UI] junit.awtui.TestRunner

    [Swing UI] junit.swingui.TestRunner

    我們前面已經看過文本界面了,下面讓我們來看一看圖形界面:

    界面很簡單,鍵入類名-testCar;蛟趩覷I的時候鍵入類名:

    [Windows] D:\>java junit.swingui.TestRunner testCar

    [Unix] % java junit.swingui.TestRunner testCar

    從圖形UI可以更好的運行測試可查單測試結果。還有一個問題需要注意:如果JUnit報告了測試沒有成功,JUnit會區分失。╢ailures)和錯誤(errors)。失敗是一個期望的被assert方法檢查到的結果。而錯誤則是意外的問題引起的,如ArrayIndexOutOfBoundsException。

    由于TestRunner十分簡單,界面也比較直觀,故不多介紹。朋友們可自行參考相關資料。

    JUnit最佳實踐

    Martin Fowler(又是這位高人)說過:“當你試圖打印輸出一些信息或調試一個表達式時,寫一些測試代碼來替代那些傳統的方法。”一開始,你會發現你總是要創建一些新的Fixture,而且測試似乎使你的編程速度慢了下來。然而不久之后,你會發現你重復使用相同的Fixture,而且新的測試通常只涉及添加一個新的測試方法。

    你可能會寫許多測試代碼,但你很快就會發現你設想出的測試只有一小部分是真正有用的。你所需要的測試是那些會失敗的測試,即那些你認為不會失敗的測試,或你認為應該失敗卻成功的測試。

    我們前面提到過測試是一個不會中斷的過程。一旦你有了一個測試,你就要一直確保其正常工作,以檢驗你所加入的新的工作代碼。不要每隔幾天或最后才運行測試,每天你都應該運行一下測試代碼。這種投資很小,但可以確保你得到可以信賴的工作代碼。你的返工率降低了,你會有更多的時間編寫工作代碼。

    不要認為壓力大,就不寫測試代碼。相反編寫測試代碼會使你的壓力逐漸減輕,應為通過編寫測試代碼,你對類的行為有了確切的認識。你會更快地編寫出有效率地工作代碼。下面是一些具體的編寫測試代碼的技巧或較好的實踐方法:

    1. 不要用TestCase的構造函數初始化Fixture,而要用setUp()和tearDown()方法;

    2. 不要依賴或假定測試運行的順序,因為JUnit利用Vector保存測試方法。所以不同的平臺會按不同的順序從Vector中取出測試方法;

    3. 避免編寫有副作用的TestCase。例如:如果隨后的測試依賴于某些特定的交易數據,就不要提交交易數據。簡單的回滾就可以了;

    4. 當繼承一個測試類時,記得調用父類的setUp()和tearDown()方法;

    5. 將測試代碼和工作代碼放在一起,一邊同步編譯和更新(使用Ant中有支持junit的task);

    6. 測試類和測試方法應該有一致的命名方案。如在工作類名前加上test從而形成測試類名;

    7. 確保測試與時間無關,不要依賴使用過期的數據進行測試。導致在隨后的維護過程中很難重現測試;

    8. 如果你編寫的軟件面向國際市場,編寫測試時要考慮國際化的因素。不要僅用母語的Locale進行測試;

    9. 盡可能地利用JUnit提供地assert/fail方法以及異常處理的方法,可以使代碼更為簡潔;

    10.測試要盡可能地小,執行速度快。

    事實上,JUnit還可用于集成測試,但我并沒涉及到,原因有兩個:一是因為沒有單元測試,集成測試無從談起。我們接受測試地概念已經很不容易了,如果再引入集成測試就會更困難。二是我比較懶,希望將集成測試的任務交給測試人員去做。在JUnit的網站上有一些相關的文章,有空大家可以翻一翻。

    JUnit與J2EE

    如果大家仔細考慮一下的話,就會發現,JUnit有自己的局限性,比如對圖形界面的測試,對servlet/JSP以及EJB的測試我們都沒有舉相關的例子。實際上,JUnit對于GUI界面,servlet/JSP,JavaBean以及EJB都有辦法測試。關于GUI的測試比較復雜,適合用一整篇文章來介紹。這里就不多說了。

    前面我們所做的測試實際上有一個隱含的環境,JVM我們的類需要這個JVM來執行。而在J2EE框架中,servlet/JSP,EJB都要求有自己的運行環境:Web Container和EJB Container。所以,要想對servlet/JSP,EJB進行測試就需要將其部署在相應的Container中才能進行測試。由于EJB不涉及UI的問題(除非EJB操作XML數據,此時的測試代碼比較難寫,有可能需要你比較兩棵DOM樹是否含有相同的內容)只要部署上去之后就可以運行測試代碼了。此時setUp()方法顯得特別有用,你可以在setUp()方法中利用JNDI查找特定的EJB。而在testXXX()方法中調用并測試這些EJB的方法。

    這里所指的JavaBean同樣沒有UI的問題,比如,我們用JavaBean來訪問數據庫,或用JavaBean來包裹EJB。如果這類JavaBean沒有用到Container的提供的服務,則可直接進行測試,同我們前面所說的一般的類的測試方法一樣。如果這類JavaBean用到了Container的提供的服務,則需要將其部署在Container中才能進行測試。方法與EJB類似。對于servlet/JSP的測試則比較棘手,有人建議在測試代碼中構造HttpRequest和HttpResponse,然后進行比較,這就要求開發人員對HTTP協議以及servlet/JSP的內部實現有比較深的認識。我認為這招不太現實。也有人提出使用HttpUnit。由于我對Cactus和HttpUnit了解不多,所以無法做出合適的建議。希望各位先知們能不吝賜教。

    正是由于JUnit的開放性和簡單易行,才會引出這篇介紹文章。但技術總在不斷地更新,而且我對測試并沒有非常深入的理解;我可以將一個復雜的概念簡化成一句非常容易理解的話。但我的本意只是希望能降低開發人員步入測試領域的門檻,而不是要修改或重新定義一些概念。這一點是特別要強調的。最后,如果有些兄弟姐妹能給我指出一些注意事項或我對某些問題的理解有誤,我會非常感激的。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>