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

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

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

    我的漫漫程序之旅

    專注于JavaWeb開發(fā)
    隨筆 - 39, 文章 - 310, 評論 - 411, 引用 - 0
    數(shù)據(jù)加載中……

    全面認(rèn)識JUnit4.0

    提要 本文將向你介紹如何實現(xiàn)從JUnit 3.8向JUnit 4的遷移。同時,還討論JUnit 4中的一些新特征,特別包括對注解的使用;最后,簡要介紹這個新版本的IDE集成現(xiàn)狀。

      一、 引言

      在本文開始,我將假定,你已經(jīng)了解由Kent Beck和Erich Gamma發(fā)明的這個Java單元測試框架并因此而略過必要的簡介。所以,我將集中分析從JUnit 3.8到最新版本-JUnit 4的遷移過程以及其在IDE和Ant中的集成。

      JUnit 4是一種與其之前的版本完全不同的API,它根據(jù)Java 5.0中的新特征(注解,靜態(tài)導(dǎo)入等)構(gòu)建而成。如你所見,JUnit 4更簡單、更豐富和更易于使用,而且它引入了更為靈活的初始化和清理工作,還有限時的和參數(shù)化測試用例。

      代碼實例最能說明問題。因此,在本文中,我將使用一個例子來展示不同的測試用例:一個計算器。該示例計算器很簡單,效率并不高,甚至還有一些錯誤;它僅僅操作整數(shù),并且把結(jié)果存儲在一個靜態(tài)變量中。Substract方法并不返回一個有效的結(jié)果,而且也沒有實現(xiàn)乘法運算,而且看上去在squareRoot方法中還存在一個錯誤:無限循環(huán)。這些錯誤將幫助說明使用JUnit 4進(jìn)行測試的有效性。你可以打開和關(guān)閉這個計算器,而且你可以清除這些結(jié)果。下面是其實現(xiàn)代碼:

    package calc;
    public class Calculator {
     private static int result; //存儲結(jié)果的靜態(tài)變量
     public void add(int n) {
      result = result + n;
     }
     public void substract(int n) {
      result = result - 1; //錯誤:應(yīng)該是"result = result - n"
     }
     public void multiply(int n) {} //還沒實現(xiàn)
     public void divide(int n) {
      result = result / n;
     }
     public void square(int n) {
      result = n * n;
     }
     public void squareRoot(int n) {
      for (; ;) ; //錯誤:無限循環(huán)
     }
     public void clear() { //清除結(jié)果
      result = 0;
     }
     public void switchOn() { //打開屏幕,顯示"hello",并報警
      result = 0; //實現(xiàn)其它的計算器功能
     }
     public void switchOff() { } //顯示"bye bye",報警,并關(guān)閉屏幕
     public int getResult() {
      return result;
     }
    }

      二、 遷移一個測試類

      現(xiàn)在,我將把一個已經(jīng)使用JUnit 3.8編寫成的簡單的測試類遷移到JUnit 4。這個類有一些缺陷:它沒有測試所有的業(yè)務(wù)方法,而且看上去在testDivide方法中還存在一個錯誤(8/2不等于5)。因為還沒有實現(xiàn)乘法運算功能,所以對其測試將被忽略。

      下面,我們把兩個版本的框架之間的差別以粗體顯示出現(xiàn)于表格1中。

      表格1.分別以JUnit 3.8和JUnit 4實現(xiàn)的CaculatorTest。

      JUnit 3.8

    package junit3;
    import calc.Calculator;
    import junit.framework.TestCase;
    public class CalculatorTest extends TestCase {
     private static Calculator calculator = new Calculator();
     @Override protected void setUp() { calculator.clear(); }
     public void testAdd() {
      calculator.add(1);
      calculator.add(1);
      assertEquals(calculator.getResult(), 2);
     }
     public void testSubtract() {
      calculator.add(10);
      calculator.subtract(2);
      assertEquals(calculator.getResult(), 8);
     }
     public void testDivide() {
      calculator.add(8);
      calculator.divide(2);
      assert calculator.getResult() == 5;
     }
     public void testDivideByZero() {
      try {
       calculator.divide(0);
       fail();
      }
      catch (ArithmeticException e) { }
     }
     public void notReadyYetTestMultiply() {
      calculator.add(10);
      calculator.multiply(10);
      assertEquals(calculator.getResult(), 100);
     }
    }

      JUnit 4

    package JUnit 4;
    import calc.Calculator;
    import org.junit.Before;
    import org.junit.Ignore;
    import org.junit.Test;
    import static org.junit.Assert.*;
    public class CalculatorTest {
     private static Calculator calculator = new Calculator();
     @Before public void clearCalculator() {
      calculator.clear();
     }
     @Test public void add() {
      calculator.add(1);
      calculator.add(1);
      assertEquals(calculator.getResult(), 2);
     }
     @Test public void subtract() {
      calculator.add(10);
      calculator.subtract(2);
      assertEquals(calculator.getResult(), 8);
     }
     @Test public void divide() {
      calculator.add(8);
      calculator.divide(2);
      assert calculator.getResult() == 5;
     }
     @Test(expected = ArithmeticException.class)
     public void divideByZero() {
      calculator.divide(0);
     }
     @Ignore("not ready yet")
     @Test
     public void multiply() {
      calculator.add(10);
      calculator.multiply(10);
      assertEquals(calculator.getResult(), 100);
     }
    }
      三、 包

      首先,你可以看到,JUnit 4使用org.junit.*包而JUnit 3.8使用的是junit.framework.*。當(dāng)然,為了向后兼容性起見,JUnit 4jar文件發(fā)行中加入了這兩種包。

      四、 繼承

      在JUnit4中,測試類不必再擴(kuò)展junit.framework.TestCase;事實上,它們不必須擴(kuò)展任何內(nèi)容。但是,JUnit 4中使用的是注解。為了以一個測試用例方式執(zhí)行,一個JUnit 4類中至少需要一個@Test注解。例如,如果你僅使用@Before和@After注解而沒有至少提供一個@Test方法來編寫一個類,那么,當(dāng)你試圖執(zhí)行它時將得到一個錯誤:

    java.lang.Exception: No runnable methods.

      五、 斷言(Assert)方法

      因為在JUnit 4中一個測試類并不繼承自TestCase(在JUnit 3.8中,這個類中定義了assertEquals()方法),所以你必須使用前綴語法(舉例來說,Assert.assertEquals())或者(由于JDK5.0)靜態(tài)地導(dǎo)入Assert類。這樣以來,你就可以完全象以前一樣使用assertEquals方法(舉例來說,assertEquals())。

      另外,在JUnit 4中,還引入了兩個新的斷言方法,它們專門用于數(shù)組對象的比較。如果兩個數(shù)組包含的元素都相等,那么這兩個數(shù)組就是相等的。

    public static void assertEquals(String message, Object[] expecteds, Object[] actuals);
    public static void assertEquals(Object[] expecteds, Object[] actuals);

      由于JDK 5.0的自動裝箱機(jī)制的出現(xiàn),原先的12個assertEquals方法全部去掉了。例如,原先JUnit 3.8中的assertEquals(long,long)方法在JUnit 4中要使用assertEquals(Object,Object)。對于assertEquals(byte,byte)、assertEquals(int,int)等也是這樣。這種改進(jìn)將有助于避免反模式。

      在JUnit 4中,新集成了一個assert關(guān)鍵字(見我們的例子中的divide()方法)。你可以象使用assertEquals方法一樣來使用它,因為它們都拋出相同的異常(java.lang.AssertionError)。JUnit 3.8的assertEquals將拋出一個junit.framework.AssertionFailedError。注意,當(dāng)使用assert時,你必須指定Java的"-ea"參數(shù);否則,斷言將被忽略。

      六、 預(yù)設(shè)環(huán)境(Fixture)

      Fixture是在測試期間初始化和釋放任何普通對象的方法。在JUnit 3.8中,你要使用setUp()來實現(xiàn)運行每一個測試前的初始化工作,然后使用tearDown()來進(jìn)行每個測試后的清理。這兩個方法在TestCase類中都得到重載,因此都被唯一定義。注意,我在這個Setup方法使用的是Java5.0內(nèi)置的@Override注解-這個注解指示該方法聲明要重載在超類中的方法聲明。在JUnit 4中,則代之使用的是@Before和@After注解;而且,可以以任何命名(在我們的例子中是clearCalculator())來調(diào)用這些方法。在本文后面,我將更多地解釋這些注解。

      七、 測試

      JUnit 3.8通過分析它的簽名來識別一個測試方法:方法名必須以"test"為前綴,它必須返回void,而且它必須沒有任何參數(shù)(舉例來說,publicvoidtestDivide())。一個不遵循這個命名約定的測試方法將被框架簡單地忽略,而且不拋出任何異常(指示發(fā)生了一個錯誤)。
    JUnit 4不使用與JUnit 3.8相同的約定。一個測試方法不必以'test'為前綴,但是要使用@Test注解。但是,正如在前一個框架中一樣,一個測試方法也必須返回void并且是無參數(shù)的。在JUnit 4中,可以在運行時刻控制這個要求,并且不符合要求的話會拋出一個異常:

    java.lang.Exception: Method xxx should have no parameters
    java.lang.Exception: Method xxx should be void

      @Test注解支持可選參數(shù)。它聲明一個測試方法應(yīng)該拋出一個異常。如果它不拋出或者如果它拋出一個與事先聲明的不同的異常,那么該測試失敗。在我們的例子中,一個整數(shù)被零除應(yīng)該引發(fā)一個ArithmeticException異常。

      八、 忽略一個測試

      記住,不能執(zhí)行多個方法。然而,如果你不想讓測試失敗的話,你可以僅僅忽略它。那么,在JUnit 3.8中,我們是如何實現(xiàn)臨時禁止一個測試的呢?方法是:通過注釋掉它或者改變命名約定,這樣測試運行機(jī)就無法找到它。在我的例子中,我使用了方法名notReadyYetTestMultiply()。它沒有以"test"開頭,所以它不會被識別出來。現(xiàn)在的問題是,在成百上千的測試中間,你可能記不住重命名這個方法。

      在JUnit 4中,為了忽略一個測試,你可以注釋掉一個方法或者刪除@Test注解(你不能再改變命名約定,否則將拋出一個異常)。然而,該問題將保留:該運行機(jī)將不報告這樣一個測試。現(xiàn)在,你可以把@Ignore注解添加到@Test的前面或者后面。測試運行機(jī)將報告被忽略的測試的個數(shù),以及運行的測試的數(shù)目和運行失敗的測試數(shù)目。注意,@Ignore使用一個可選參數(shù)(一個String),如果你想記錄為什么一個測試被忽略的話。

      九、 運行測試

      在JUnit 3.8中,你可以選擇使用若干運行機(jī):文本型,AWT或者Swing。JUnit 4僅僅使用文本測試運行機(jī)。注意,JUnit 4不會顯示任何綠色條來通知你測試成功了。如果你想看到任何類型的綠色的話,那么你可能需要使用JUnit擴(kuò)展或一種集成了JUnit的IDE(例如IDEA或者Eclipse)。

      首先,我想使用老式但好用的junit.textui.TestRunner來運行該JUnit 3.8測試類(考慮到使用assert關(guān)鍵字,我使用了-ea參數(shù))。
    java -ea junit.textui.TestRunner junit3.CalculatorTest

    ..F.E.
    There was 1 error:
    1) testDivide(junit3.CalculatorTest)java.lang.AssertionError
    at junit3.CalculatorTest.testDivide(CalculatorTest.java:33)
    There was 1 failure:
    1) testSubtract(junit3.CalculatorTest)junit.framework.AssertionFailedError: expected:<9> but was:<8>
    at junit3.CalculatorTest.testSubtract(CalculatorTest.java:27)
    FAILURES!!!
    Tests run: 4, Failures: 1, Errors: 1

      TestDivide產(chǎn)生一個錯誤,因為斷言確定了8/2不等于5。TestSubstract產(chǎn)生一個失敗,因為10-2應(yīng)該等于8,但是在這個實現(xiàn)中存在一個錯誤:它返回9。

      現(xiàn)在,我使用新的org.junit.runner.JUnitCore運行機(jī)來運行這兩個類。注意,它能執(zhí)行JUnit 4和JUnit 3.8測試,甚至是這二者的結(jié)合。

    java -ea org.junit.runner.JUnitCore junit3.CalculatorTest

    JUnit version 4.1

    ..E.E.
    There were 2 failures:
    1) testSubtract(junit3.CalculatorTest)
    junit.framework.AssertionFailedError: expected:<9> but was:<8>
    at junit.framework.Assert.fail(Assert.java:47)
    2) testDivide(junit3.CalculatorTest)
    java.lang.AssertionError
    at junit3.CalculatorTest.testDivide(CalculatorTest.java:33)
    FAILURES!!!
    Tests run: 4, Failures: 2
    ***

    java -ea org.junit.runner.JUnitCore JUnit 4.CalculatorTest

    JUnit version 4.1
    ...E.EI
    There were 2 failures:
    1) subtract(JUnit 4.CalculatorTest)
    java.lang.AssertionError: expected:<9> but was:<8>
    at org.junit.Assert.fail(Assert.java:69)
    2) divide(JUnit 4.CalculatorTest)
    java.lang.AssertionError
    at JUnit 4.CalculatorTest.divide(CalculatorTest.java:40)
    FAILURES!!!
    Tests run: 4, Failures: 2

      第一個非常明顯的區(qū)別是,JUnit版本號被顯示于控制臺中(4.1)。第二個區(qū)別是,JUnit 3.8區(qū)分失敗和錯誤;JUnit 4則僅使用失敗進(jìn)行簡化。一個新奇的地方是,字母"I",它顯示一個測試被忽略。
     十、 高級測試

      現(xiàn)在,我將展示JUnit 4的一些高級特征。列表1(見下載源碼)是一個新的測試類-AdvancedTest,它派生自AbstractParent。

      (一) 高級預(yù)設(shè)環(huán)境

      兩個類都使用新的注解@BeforeClass和@AfterClass,還有@Before和@After。表格2展示了在這些注解之間的主要區(qū)別。

      表格2.@BeforeClass/@AfterClass比較于@Before/@After。

    @BeforeClass和@AfterClass @Before和@After
    在每個類中只有一個方法能被注解。 多個方法能被注解,但其執(zhí)行的順序未特別指定,且不運行重載方法。
    方法名是不相關(guān)的 方法名是不相關(guān)的
    每個類運行一次 在每個測試方法運行前或運行后運行
    在當(dāng)前類的@BeforeClass方法運行前先運行超類的@BeforeClass方法。在超類中聲明的@AfterClass方法將在所有當(dāng)前類的該方法運行后才運行。 超類中的@Before在所有子類的該方法運行前運行。在超類中的@After在在所有子類的該方法運行后才運行。
    必須是公共和非靜態(tài)的。 必須是公共和非靜態(tài)的。
    即使一個@BeforeClass方法拋出一個異常,所有的@AfterClass方法也保證被運行。 即使一個@Before或者@Test方法拋出一個異常,所有的@After方法也保證被運行。

      如果你僅有一次需要分配和釋放昂貴的資源,那么@BeforeClass和@AfterClass可能很有用。在我們的例子中,AbstractParent使用這些在startTestSystem()和stopTestSystem()方法上的注解啟動和停止整個測試系統(tǒng)。并且它使用@Before和@After初始化和清除系統(tǒng)。子類AdvancedTest也混合使用這些注解。

      在你的測試代碼中使用System.out.println不是一種良好的實踐習(xí)慣;但是,在這個用例中,它有助于理解這些注解被調(diào)用的順序。當(dāng)我運行AdvancedTest時,我得到如下結(jié)果:

    Start test system //父類的@BeforeClass
    Switch on calculator //子類的@BeforeClass

    Initialize test system //第一個測試
    Clear calculator

    Initialize test system //第二個測試
    Clear calculator
    Clean test system

    Initialize test system //第三個測試
    Clear calculator
    Clean test system

    Initialize test system //第四個測試
    Clear calculator
    Clean test system

    Switch off calculator //子類的@AfterClass
    Stop test system //父類的@AfterClass

      如你所見,@BeforeClass和@AfterClass僅被調(diào)用一次,而@Before和@Afterare在每次測試中都要調(diào)用。

      (二) 限時測試

      在前面的例子中,我為squareRoot()方法編寫了一個測試用例。記住,在這個方法中存在一個錯誤-能夠?qū)е滤鼰o限循環(huán)。如果沒有結(jié)果的話,我想讓這個測試在1秒鐘后退出。這一功能正是timeout參數(shù)所要實現(xiàn)的。@Test注解的第二個可選參數(shù)(第一個參數(shù)是必需的)可以使一個測試失敗,如果該測試花費比一個預(yù)先確定的時限(毫秒)還長的時間的話。當(dāng)我運行該測試時,我得到如下的運行結(jié)果:

    There was 1 failure:

    1) squareRoot(JUnit 4.AdvancedTest)
    java.lang.Exception: test timed out after 1000 milliseconds
    at org.junit.internal.runners.TestMethodRunner.runWithTimeout(TestMethodRunner.java:68)
    at org.junit.internal.runners.TestMethodRunner.運行(TestMethodRunner.java:43)

    FAILURES!!!
    Tests run: 4, Failures: 1

      (三) 參數(shù)化測試

      在列表1中,我測試了squareRoot(它是square方法而不是squareRoot方法)-通過創(chuàng)建若干測試方法(square2,square4,square5),這些方法都完成相同的事情(通過被一些變量參數(shù)化實現(xiàn))。其實,現(xiàn)在這里的復(fù)制/粘貼技術(shù)可以通過使用一個參數(shù)化測試用例加以優(yōu)化(列表2)。

      在列表2(見本文相應(yīng)下載源碼)中的測試用例使用了兩個新的注解。當(dāng)一個類被使用@RunWith注釋時,JUnit將調(diào)用被參考的類來運行該測試而不是使用缺省的運行機(jī)。為了使用一個參數(shù)化測試用例,你需要使用運行機(jī)org.junit.runners.Parameterized。為了確定使用哪個參數(shù),該測試用例需要一個公共靜態(tài)方法(在此是data(),但是名字似乎無關(guān)),該方法返回一個Collection,并且被使用@參數(shù)加以注解。你還需要一個使用這些參數(shù)的公共構(gòu)造函數(shù)。

      當(dāng)運行這個類,該輸出是:

    java org.junit.runner.JUnitCore JUnit 4.SquareTest
    JUnit version 4.1

    .......E

    There was 1 failure:
    1) square[6](JUnit 4.SquareTest)
    java.lang.AssertionError: expected:<48> but was:<49>
    at org.junit.Assert.fail(Assert.java:69)

    FAILURES!!!
    Tests run: 7, Failures: 1

      在此,共執(zhí)行了7個測試,好象編寫了7個單獨的square方法。注意,在我們的測試中出現(xiàn)了一個失敗,因為7的平方是49,而不是48。

      (四) 測試集

      為了在JUnit 3.8的一個測試集中運行若干測試類,你必須在你的類中添加一個suite()方法。而在JUnit 4中,你可以使用注解來代之。為了運行CalculatorTest和SquareTest,你需要使用@RunWith和@Suite注解編寫一個空類。

    package JUnit 4;

    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;

    @RunWith(Suite.class)
    @Suite.SuiteClasses({
    CalculatorTest.class,
    SquareTest.class
    })
    public class AllCalculatorTests {}
    java -ea org.junit.runner.JUnitCore JUnit 4.AllCalculatorTests
    JUnit version 4.1
    ...E.EI.......E
    There were 3 failures:
    1) subtract(JUnit 4.CalculatorTest)
    java.lang.AssertionError: expected:<9> but was:<8>
    at org.junit.Assert.fail(Assert.java:69)
    2) divide(JUnit 4.CalculatorTest)
    java.lang.AssertionError
    at JUnit 4.CalculatorTest.divide(CalculatorTest.java:40)
    3) square[6](JUnit 4.SquareTest)
    java.lang.AssertionError: expected:<48> but was:<49>
    at org.junit.Assert.fail(Assert.java:69)
    FAILURES!!!
    Tests run: 11, Failures: 3

      (五) 測試運行機(jī)

      在JUnit 4中,廣泛地使用測試運行機(jī)。如果沒有指定@RunWith,那么你的類仍然會使用一個默認(rèn)運行機(jī)(org.junit.internal.runners.TestClassRunner)執(zhí)行。注意,最初的Calculator類中并沒有顯式地聲明一個測試運行機(jī);因此,它使用的是默認(rèn)運行機(jī)。一個包含一個帶有@Test的方法的類都隱含地?fù)碛幸粋€@RunWith。事實上,你可以把下列代碼添加到Calculator類上,而且其輸出結(jié)果會完全一樣。

    import org.junit.internal.runners.TestClassRunner;
    import org.junit.runner.RunWith;
    @RunWith(TestClassRunner.class)
    public class CalculatorTest {
    ...
    }

      在@Parameterized和@Suite的情況下,我需要一個特定的運行機(jī)來執(zhí)行我的測試用例。這就是為什么我顯式地注解了它們。
     十一、 工具集成功能

      當(dāng)我寫本文時,JUnit 4在IDE方面的集成還不是很理想。事實上,如果你試圖運行我們剛才看到的那個測試類的話,它們無法工作在任何IDE環(huán)境中,因為它們不能被識別為測試類。為了向前兼容性起見,JUnit 4發(fā)行中帶有一個適配器(junit.framework.JUnit 4TestAdapter),你必須把它使用于一個suite()方法中。下面是你必須添加到每個類中的代碼;這樣以來,它們才能為各種IDE,Ant以及JUnit 3.8中的文本運行機(jī)所識別:

    public static junit.framework.Test suite() {
     return new JUnit 4TestAdapter(CalculatorTest.class);
    }

      (一) Intellij IDEA

      IDEA 5并沒有集成JUnit 4。沒有辦法,我們只好等待IDEA 6中實現(xiàn)這一集成了。在這個例子中,我使用了較早的發(fā)行版本(Demetra build 5321);但是,參數(shù)化測試用例仍不能工作。圖1展示了這個CalculatorTest的執(zhí)行情況(被忽略的測試以一個不同的圖標(biāo)標(biāo)志)。


    圖1.IDEADemetra僅能運行CalculatorTest。

    圖2.Eclipse 3.2RC7能夠運行測試集類AllCalculatorTests。

      (二) Eclipse

      我現(xiàn)在使用的是Eclipse的3.2 RC7版本。雖然它還不是一個穩(wěn)定發(fā)行版本,但是其與JUnit 4的集成優(yōu)于IDEA。上圖2展示了在運行AllCalculatorTests類時你能看到的結(jié)果。

      如你所見,該參數(shù)化測試用例(SquareTest)被描述為7個單獨的測試。

      (三) Ant集成

      junit任務(wù)當(dāng)前僅僅支持JUnit 3.8風(fēng)格測試;這也就是說,你還必須用一個JUnit 4 TestAdapter來包裝你的JUnit 4測試;這樣,它們才能在Ant中運行。這個<junit>任務(wù)與其在JUnit 3.8中用法一樣:

    <!-- Test -->
    <target name="test" depends="compile">
    <junit fork="yes" haltonfailure="yes">
    <test name=" JUnit 4.AllCalculatorTests"/>
    <formatter type="plain" usefile="false"/>
    <classpath refid="classpath"/>
    </junit>
    </target>

      十二、 結(jié)論

      有很長一段時間,JUnit簡直成了事實上的單元測試框架標(biāo)準(zhǔn)。但是,近來,這個框架似乎無大"動靜":沒有重要的發(fā)行版本,沒有引人注目的新特征出現(xiàn)。這可能是為什么其它測試框架,例如Test-NG開始逐漸占居測試框架市場統(tǒng)治地位的原因。

      隨著這個新版本的發(fā)行,JUnit又出現(xiàn)了新的轉(zhuǎn)機(jī)。如今,它提供了許多新的API,而且現(xiàn)在還使用注解,所以使開發(fā)測試用例更為容易。事實上,該JUnit開發(fā)者已經(jīng)開始考慮新的未來的注解問題。例如,你可以在一個依賴于前提(舉例來說,你需要在線地執(zhí)行這個測試)的測試用例上添加一個@Prerequisite注解;或者添加一個能夠指定重復(fù)次數(shù)及時限(舉例來說,重復(fù)測試5次以確保真正出現(xiàn)了一個時限問題)的@Repeat注解;或者甚至在@Ignore注解上添加一個平臺參數(shù)(舉例來說,@Ignore(platform=macos),這將只有你在一個MacOS平臺上運行時才忽略一個測試)。從本文中你能看到,JUnit的未來依然燦爛輝煌。


    posted on 2007-11-24 08:43 々上善若水々 閱讀(13108) 評論(1)  編輯  收藏 所屬分類: 軟件測試

    評論

    # re: 全面認(rèn)識JUnit4.0  回復(fù)  更多評論   

    java junit代碼下載地址:http://www.zuidaima.com/share/search.htm?key=junit
    2014-05-30 00:01 | zuidaima
    主站蜘蛛池模板: 亚洲好看的理论片电影| 免费观看美女用震蛋喷水的视频| 亚洲不卡中文字幕| 亚洲中文字幕在线观看| 日本人的色道www免费一区| 久久伊人免费视频| 中文字幕免费观看视频| 色欲aⅴ亚洲情无码AV蜜桃| 亚洲男女一区二区三区| 亚洲国产精品一区二区第一页| 又粗又硬又黄又爽的免费视频 | 国产亚洲欧洲Aⅴ综合一区| 女人与禽交视频免费看| 国产成人精品久久免费动漫| 一区二区三区无码视频免费福利| 一级做a毛片免费视频| 国产精品亚洲一区二区三区久久| 在线a亚洲老鸭窝天堂av高清| 亚洲精品韩国美女在线| 亚洲无删减国产精品一区| 亚洲AV综合色区无码一区 | 国产综合免费精品久久久| 黄色免费网址在线观看| 亚洲成人激情小说| 亚洲乱码一二三四区乱码| 亚洲国产成人精品无码区在线网站 | 亚洲日韩中文字幕日韩在线| 国产免费69成人精品视频 | 国产精品亚洲片在线花蝴蝶 | 免费人成网站7777视频| 在线观着免费观看国产黄| 全免费一级午夜毛片| 性感美女视频在线观看免费精品 | 亚洲av无码专区在线电影天堂 | 久久久久久夜精品精品免费啦 | 久久亚洲熟女cc98cm| 久久亚洲sm情趣捆绑调教| 亚洲视频在线不卡| 亚洲精品人成电影网| 亚洲国产成人久久精品app| 精品亚洲AV无码一区二区|