<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Vincent.Chan‘s Blog

    常用鏈接

    統計

    積分與排名

    網站

    最新評論

    JUnit 4 搶先看:即將發布的版本承諾在測試方面有新的進展

    級別: 中級

    Elliotte Harold, 副教授, Polytechnic University

    2005 年 10 月 13 日

    JUnit 是 Java? 語言事實上的 標準單元測試庫。JUnit 4 是該庫三年以來最具里程碑意義的一次發布。它的新特性主要是通過采用 Java 5 中的標記(annotation)而不是利用子類、反射或命名機制來識別測試,從而簡化測試。在本文中,執著的代碼測試人員 Elliotte Harold 以 JUnit 4 為例,詳細介紹了如何在自己的工作中使用這個新框架。注意,本文假設讀者具有 JUnit 的使用經驗。

    JUnit 由 Kent Beck 和 Erich Gamma 開發,幾乎毫無疑問是迄今所開發的最重要的第三方 Java 庫。正如 Martin Fowler 所說,“在軟件開發領域,從來就沒有如此少的代碼起到了如此重要的作用”。JUnit 引導并促進了測試的盛行。由于 JUnit,Java 代碼變得更健壯,更可靠,bug 也比以前更少。JUnit(它本身的靈感來自 Smalltalk 的 SUnit)衍生了許多 xUnit 工具,將單元測試的優勢應用于各種語言。nUnit (.NET)、pyUnit (Python)、CppUnit (C++)、dUnit (Delphi) 以及其他工具,影響了各種平臺和語言上的程序員的測試工作。

    然而,JUnit 僅僅是一個工具而已。真正的優勢來自于 JUnit 所采用的思想和技術,而不是框架本身。單元測試、測試先行的編程和測試驅動的開發并非都要在 JUnit 中實現,任何比較 GUI 的編程都必須用 Swing 來完成。JUnit 本身的最后一次更新差不多是三年以前了。盡管它被證明比大多數框架更健壯、更持久,但是也發現了 bug;而更重要的是,Java 不斷在發展。Java 語言現在支持泛型、枚舉、可變長度參數列表和注釋,這些特性為可重用的框架設計帶來了新的可能。

    JUnit 的停滯不前并沒有被那些想要廢棄它的程序員所打敗。挑戰者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等。這些庫有一些可圈可點的特性,但是都沒有達到 JUnit 的知名度和市場占有份額。它們都沒有在諸如 Ant、Maven 或 Eclipse 之類的產品中具有廣泛的開箱即用支持。所以 Beck 和 Gamma 著手開發了一個新版本的 JUnit,它利用 Java 5 的新特性(尤其是注釋)的優勢,使得單元測試比起用最初的 JUnit 來說更加簡單。用 Beck 的話來說,“JUnit 4 的主題是通過進一步簡化 JUnit,鼓勵更多的開發人員編寫更多的測試。”JUnit 4 盡管保持了與現有 JUnit 3.8 測試套件的向后兼容,但是它仍然承諾是自 JUnit 1.0 以來 Java 單元測試方面最重大的改進。

    注意:該框架的改進是相當前沿的。盡管 JUnit 4 的大輪廓很清晰,但是其細節仍然可以改變。這意味著本文是對 JUnit 4 搶先看,而不是它的最終效果。

    測試方法

    以前所有版本的 JUnit 都使用命名約定和反射來定位測試。例如,下面的代碼測試 1+1 等于 2:

    												
    														
    import junit.framework.TestCase;

    public class AdditionTest extends TestCase {

    private int x = 1;
    private int y = 1;

    public void testAddition() {
    int z = x + y;
    assertEquals(2, z);
    }

    }

    而在 JUnit 4 中,測試是由 @Test 注釋來識別的,如下所示:

    												import org.junit.Test;
    import junit.framework.TestCase;

    public class AdditionTest extends TestCase {

    private int x = 1;
    private int y = 1;

    @Test public void testAddition() {
    int z = x + y;
    assertEquals(2, z);
    }

    }

    使用注釋的優點是不再需要將所有的方法命名為 testFoo()testBar(),等等。例如,下面的方法也可以工作:

    												import org.junit.Test;
    import junit.framework.TestCase;

    public class AdditionTest extends TestCase {

    private int x = 1;
    private int y = 1;

    @Test public void additionTest() {
    int z = x + y;
    assertEquals(2, z);
    }

    }

    下面這個方法也同樣能夠工作:

    												import org.junit.Test;
    import junit.framework.TestCase;

    public class AdditionTest extends TestCase {

    private int x = 1;
    private int y = 1;

    @Test public void addition() {
    int z = x + y;
    assertEquals(2, z);
    }

    }

    這允許您遵循最適合您的應用程序的命名約定。例如,我介紹的一些例子采用的約定是,測試類對其測試方法使用與被測試的類相同的名稱。例如,List.contains()ListTest.contains() 測試,List.add()ListTest.addAll() 測試,等等。

    TestCase 類仍然可以工作,但是您不再需要擴展它了。只要您用 @Test 來注釋測試方法,就可以將測試方法放到任何類中。但是您需要導入 junit.Assert 類以訪問各種 assert 方法,如下所示:

    												import org.junit.Assert;

    public class AdditionTest {

    private int x = 1;
    private int y = 1;

    @Test public void addition() {
    int z = x + y;
    Assert.assertEquals(2, z);
    }

    }

    您也可以使用 JDK 5 中新特性(static import),使得與以前版本一樣簡單:

    												
    														
    import static org.junit.Assert.assertEquals;

    public class AdditionTest {

    private int x = 1;
    private int y = 1;

    @Test public void addition() {
    int z = x + y;
    assertEquals(2, z);
    }

    }

    這種方法使得測試受保護的方法非常容易,因為測試案例類現在可以擴展包含受保護方法的類了。





    回頁首


    SetUp 和 TearDown

    JUnit 3 測試運行程序(test runner)會在運行每個測試之前自動調用 setUp() 方法。該方法一般會初始化字段,打開日志記錄,重置環境變量,等等。例如,下面是摘自 XOM 的 XSLTransformTest 中的 setUp() 方法:

    												protected void setUp() {

    System.setErr(new PrintStream(new ByteArrayOutputStream()));

    inputDir = new File("data");
    inputDir = new File(inputDir, "xslt");
    inputDir = new File(inputDir, "input");

    }

    在 JUnit 4 中,您仍然可以在每個測試方法運行之前初始化字段和配置環境。然而,完成這些操作的方法不再需要叫做 setUp(),只要用 @Before 注釋來指示即可,如下所示:

    												
    														
    @Before protected void initialize() {

    System.setErr(new PrintStream(new ByteArrayOutputStream()));

    inputDir = new File("data");
    inputDir = new File(inputDir, "xslt");
    inputDir = new File(inputDir, "input");

    }

    甚至可以用 @Before 來注釋多個方法,這些方法都在每個測試之前運行:

    												
    														
    @Before protected void findTestDataDirectory() {
    inputDir = new File("data");
    inputDir = new File(inputDir, "xslt");
    inputDir = new File(inputDir, "input");
    }

    @Before protected void redirectStderr() {
    System.setErr(new PrintStream(new ByteArrayOutputStream()));
    }

    清除方法與此類似。在 JUnit 3 中,您使用 tearDown() 方法,該方法類似于我在 XOM 中為消耗大量內存的測試所使用的方法:

    												protected void tearDown() {
    doc = null;
    System.gc();
    }

    對于 JUnit 4,我可以給它取一個更自然的名稱,并用 @After 注釋它:

    												
    														
    @After protected void disposeDocument() {
    doc = null;
    System.gc();
    }

    @Before 一樣,也可以用 @After 來注釋多個清除方法,這些方法都在每個測試之后運行。

    最后,您不再需要在超類中顯式調用初始化和清除方法,只要它們不被覆蓋即可,測試運行程序將根據需要自動為您調用這些方法。超類中的 @Before 方法在子類中的 @Before 方法之前被調用(這反映了構造函數調用的順序)。@After 方法以反方向運行:子類中的方法在超類中的方法之前被調用。否則,多個 @Before@After 方法的相對順序就得不到保證。

    套件范圍的初始化

    JUnit 4 也引入了一個 JUnit 3 中沒有的新特性:類范圍的 setUp()tearDown() 方法。任何用 @BeforeClass 注釋的方法都將在該類中的測試方法運行之前剛好運行一次,而任何用 @AfterClass 注釋的方法都將在該類中的所有測試都運行之后剛好運行一次。

    例 如,假設類中的每個測試都使用一個數據庫連接、一個網絡連接、一個非常大的數據結構,或者還有一些對于初始化和事情安排來說比較昂貴的其他資源。不要在每 個測試之前都重新創建它,您可以創建它一次,并還原它一次。該方法將使得有些測試案例運行起來快得多。例如,當我測試調用第三方庫的代碼中的錯誤處理時, 我通常喜歡在測試開始之前重定向 System.err,以便輸出不被預期的錯誤消息打亂。然后我在測試結束后還原它,如下所示:

    												
    														
    // This class tests a lot of error conditions, which
    // Xalan annoyingly logs to System.err. This hides System.err
    // before each test and restores it after each test.
    private PrintStream systemErr;

    @BeforeClass protected void redirectStderr() {
    systemErr = System.err; // Hold on to the original value
    System.setErr(new PrintStream(new ByteArrayOutputStream()));
    }

    @AfterClass protected void tearDown() {
    // restore the original value
    System.setErr(systemErr);
    }

    沒有必要在每個測試之前和之后都這樣做。但是一定要小心對待這個特性。它有可能會違反測試的獨立性,并引入非預期的混亂。如果一個測試在某種程度上改變了 @BeforeClass 所初始化的一個對象,那么它有可能會影響其他測試的結果。它有可能在測試套件中引入順序依賴,并隱藏 bug。與任何優化一樣,只在剖析和基準測試證明您具有實際的問題之后才實現這一點。這就是說,我看到了不止一個測試套件運行時間如此之長,以至不能像它 所需要的那樣經常運行,尤其是那些需要建立很多網絡和數據庫連接的測試。(例如,LimeWire 測試套件運行時間超過兩小時。)要加快這些測試套件,以便程序員可以更加經常地運行它們,您可以做的就是減少 bug。





    回頁首


    測試異常

    異常測試是 JUnit 4 中的最大改進。舊式的異常測試是在拋出異常的代碼中放入 try 塊,然后在 try 塊的末尾加入一個 fail() 語句。例如,該方法測試被零除拋出一個 ArithmeticException

    												
    														
    public void testDivisionByZero() {

    try {
    int n = 2 / 0;
    fail("Divided by zero!");
    }
    catch (ArithmeticException success) {
    assertNotNull(success.getMessage());
    }

    }

    該方法不僅難看,而且試圖挑戰代碼覆蓋工具,因為不管測試是通過還是失敗,總有一些代碼不被執行。在 JUnit 4 中,您現在可以編寫拋出異常的代碼,并使用注釋來聲明該異常是預期的:

    												
    														
    @Test(expected=ArithmeticException.class)
    public void divideByZero() {
    int n = 2 / 0;
    }

    如果該異常沒有拋出(或者拋出了一個不同的異常),那么測試就將失敗。但是如果您想要測試異常的詳細消息或其他屬性,則仍然需要使用舊式的 try-catch 樣式。





    回頁首


    被忽略的測試

    也 許您有一個測試運行的時間非常地長。不是說這個測試應該運行得更快,而是說它所做的工作從根本上比較復雜或緩慢。需要訪問遠程網絡服務器的測試通常都屬于 這一類。如果您不在做可能會中斷該類測試的事情,那么您可能想要跳過運行時間長的測試方法,以縮短編譯-測試-調試周期。或者也許是一個因為超出您的控制 范圍的原因而失敗的測試。例如,W3C XInclude 測試套件測試 Java 還不支持的一些 Unicode 編碼的自動識別。不必老是被迫盯住那些紅色波浪線,這類測試可以被注釋為 @Ignore,如下所示:

    												
    														
    // Java doesn't yet support
    // the UTF-32BE and UTF32LE encodings
    @Ignore public void testUTF32BE()
    throws ParsingException, IOException, XIncludeException {

    File input = new File(
    "data/xinclude/input/UTF32BE.xml"
    );
    Document doc = builder.build(input);
    Document result = XIncluder.resolve(doc);
    Document expectedResult = builder.build(
    new File(outputDir, "UTF32BE.xml")
    );
    assertEquals(expectedResult, result);

    }

    測試運行程序將不運行這些測試,但是它會指出這些測試被跳過了。例如,當使用文本界面時,會輸出一個“I”(代表 ignore),而不是為通過的測試輸出所經歷的時間,也不是為失敗的測試輸出“E”:

    												
    														
    $ java -classpath .:junit.jar org.junit.runner.JUnitCore
    nu.xom.tests.XIncludeTest
    JUnit version 4.0rc1
    .....I..
    Time: 1.149

    OK (7 tests)

    但是一定要小心。最初編寫這些測試可能有一定的原因。如果永遠忽略這些測試,那么它們期望測試的代碼可能會中斷,并且這樣的中斷可能不能被檢測到。忽略測試只是一個權宜之計,不是任何問題的真正解決方案。





    回頁首


    時間測試

    測 試性能是單元測試最為痛苦的方面之一。JUnit 4 沒有完全解決這個問題,但是它對這個問題有所幫助。測試可以用一個超時參數來注釋。如果測試運行的時間超過指定的毫秒數,則測試失敗。例如,如果測試花費 超過半秒時間去查找以前設置的一個文檔中的所有元素,那么該測試失敗:

    												
    														
    @Test(timeout=500) public void retrieveAllElementsInDocument() {
    doc.query("http://*");
    }

    除 了簡單的基準測試之外,時間測試也對網絡測試很有用。在一個測試試圖連接到的遠程主機或數據庫宕機或變慢時,您可以忽略該測試,以便不阻塞所有其他的測 試。好的測試套件執行得足夠快,以至程序員可以在每個測試發生重大變化之后運行這些測試,有可能一天運行幾十次。設置一個超時使得這一點更加可行。例如, 如果解析 http://www.ibiblio.org/xml 花費了超過 2 秒,那么下面的測試就會超時:

    												
    														
    @Test(timeout=2000)
    public void remoteBaseRelativeResolutionWithDirectory()
    throws IOException, ParsingException {
    builder.build("http://www.ibiblio.org/xml");
    }





    回頁首


    新的斷言

    JUnit 4 為比較數組添加了兩個 assert() 方法:

    												
    														
    public static void assertEquals(Object[] expected, Object[] actual)
    public static void assertEquals(String message, Object[] expected,
    Object[] actual)

    這兩個方法以最直接的方式比較數組:如果數組長度相同,且每個對應的元素相同,則兩個數組相等,否則不相等。數組為空的情況也作了考慮。





    回頁首


    需要補充的地方

    JUnit 4 基本上是一個新框架,而不是舊框架的升級版本。JUnit 3 開發人員可能會找到一些原來沒有的特性。

    最明顯的刪節就是 GUI 測試運行程序。如果您想在測試通過時看到賞心悅目的綠色波浪線,或者在測試失敗時看到令人焦慮的紅色波浪線,那么您需要一個具有集成 JUnit 支持的 IDE,比如 Eclipse。不管是 Swing 還是 AWT 測試運行程序都不會被升級或捆綁到 JUnit 4 中。

    下一個驚喜是,失敗(assert 方法檢測到的預期的錯誤)與錯誤(異常指出的非預期的錯誤)之間不再有任何差別。盡管 JUnit 3 測試運行程序仍然可以區別這些情況,而 JUnit 4 運行程序將不再能夠區分。

    最后,JUnit 4 沒有 suite() 方法,這些方法用于從多個測試類構建一個測試套件。相反,可變長參數列表用于允許將不確定數量的測試傳遞給測試運行程序。

    我對消除了 GUI 測試運行程序并不感到太高興,但是其他更改似乎有可能增加 JUnit 的簡單性。只要考慮有多少文檔和 FAQ 當前專門用于解釋這幾點,然后考慮對于 JUnit 4,您不再需要解釋這幾點了。





    回頁首


    編譯和運行 JUnit 4

    當前,還沒有 JUnit 4 的庫版本。如果您想要體驗新的版本,那么您需要從 SourceForge 上的 CVS 知識庫獲取它。分支(branch)是“Version4”(參見 參考資料)。注意,很多的文檔沒有升級,仍然是指以舊式的 3.x 方式做事。Java 5 對于編譯 JUnit 4 是必需的,因為 JUnit 4 大量用到注釋、泛型以及 Java 5 語言級的其他特性。

    自 JUnit 3 以來,從命令行運行測試的語法發生了一點變化。您現在使用 org.junit.runner.JUnitCore 類:

    												
    														
    $ java -classpath .:junit.jar org.junit.runner.JUnitCore
    TestA TestB TestC...
    JUnit version 4.0rc1

    Time: 0.003

    OK (0 tests)

    兼容性

    Beck 和 Gamma 努力維持向前和向后兼容。JUnit 4 測試運行程序可以運行 JUnit 3 測試,不用做任何更改。只要將您想要運行的每個測試的全限定類名傳遞給測試運行程序,就像針對 JUnit 4 測試一樣。運行程序足夠智能,可以分辨出哪個測試類依賴于哪個版本的 JUnit,并適當地調用它。

    向后兼容要困難一些,但是也可以在 JUnit 3 測試運行程序中運行 JUnit 4 測試。這一點很重要,所以諸如 Eclipse 之類具有集成 JUnit 支持的工具可以處理 JUnit 4,而不需要更新。為了使 JUnit 4 測試可以運行在 JUnit 3 環境中,可以將它們包裝在 JUnit4TestAdapter 中。將下面的方法添加到您的 JUnit 4 測試類中應該就足夠了:

    												
    														
    public static junit.framework.Test suite() {
    return new JUnit4TestAdapter(AssertionTest.class);
    }

    但是由于 Java 比較多變,所以 JUnit 4 一點都不向后兼容。JUnit 4 完全依賴于 Java 5 特性。對于 Java 1.4 或更早版本,它將不會編譯或運行。





    回頁首


    前景

    JUnit 4 遠沒有結束。很多重要的方面沒有提及,包括大部分的文檔。我不推薦現在就將您的測試套件轉換成注釋和 JUnit 4。即使如此,開發仍在快速進行,并且 JUnit 4 前景非常看好。盡管 Java 2 程序員在可預見的未來仍然需要使用 JUnit 3.8,但是那些已經轉移到 Java 5 的程序員則應該很快考慮使他們的測試套件適合于這個新的框架,以便匹配。





    回頁首


    參考資料

    學習

    獲得產品和技術
    • JUnit 4:下載最新版本的 JUnit,即 SourceForge CVS 知識庫。確保使用分支“Version4”。


    討論




    回頁首


    關于作者


    Elliotte Rusty Harold 出生在新奧爾良,現在,他還定期回老家喝一碗美味的秋葵湯。不過目前他與妻子 Beth 定居在紐約臨近布魯克林的 Prospect Heights,與他們住在一起的還有貓咪 Charm(取自夸克)和 Marjorie(按照他岳母的名字)。他是 Polytechnic 大學計算機科學的副教授,講授 Java 技術和面向對象編程。他的 Cafe au Lait 網站是 Internet 上最受歡迎的獨立 Java 站點之一,姊妹站點 Cafe con Leche 是最受歡迎的 XML 站點之一。他的著作包括 Effective XMLProcessing XML with JavaJava Network ProgrammingThe XML 1.1 Bible。他目前在研究處理 XML 的 XOM API、Jaxen XPath 引擎和 Jester 測試覆蓋工具。

    posted on 2006-03-18 20:54 Vincent.Chen 閱讀(224) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 嫩草视频在线免费观看| 日本免费一区二区三区| 国产精品视频免费一区二区三区| 亚洲国产亚洲片在线观看播放| 1000部啪啪毛片免费看| 亚洲国产精品自在在线观看| 免费无码又爽又刺激高潮视频| 亚洲国产成人久久综合一 | 亚洲欧洲综合在线| 日产乱码一卡二卡三免费| 亚洲久悠悠色悠在线播放| 操美女视频免费网站| 亚洲狠狠婷婷综合久久| 午夜亚洲国产成人不卡在线| 亚洲酒色1314狠狠做| 亚洲欧洲免费无码| 国产大陆亚洲精品国产| 国产亚洲美女精品久久久| 日本人成在线视频免费播放| 内射无码专区久久亚洲| eeuss影院免费92242部| 亚洲狠狠综合久久| 成人性生交大片免费看无遮挡| 亚洲AV无码久久精品色欲| 亚洲美女视频免费| 亚洲国产精品精华液| 亚洲一级特黄无码片| 男人的天堂网免费网站| 亚洲男人天堂2022| 久久精品亚洲男人的天堂| 91精品国产免费| 亚洲国产精品网站在线播放| 国产综合精品久久亚洲| 91精品免费在线观看| 国产精品亚洲一区二区在线观看| 亚洲中文字幕无码一区二区三区 | 成年男女免费视频网站| 一个人看www免费高清字幕| 久久精品蜜芽亚洲国产AV| 免费一级毛片女人图片| 18以下岁毛片在免费播放|