其實(shí)我是一個(gè)程序員  
          日歷
          <2010年11月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011
          統(tǒng)計(jì)
          • 隨筆 - 4
          • 文章 - 1
          • 評論 - 3
          • 引用 - 0

          導(dǎo)航

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

           

           

          1. OverView

          毋庸置疑,程序員要對自己編寫的代碼負(fù)責(zé),您不僅要保證它能通過編譯,正常地運(yùn)行,而且要滿足需求和設(shè)計(jì)預(yù)期的效果。單元測試正是驗(yàn)證代碼行為是否 滿足預(yù)期的有效手段之一。但不可否認(rèn),做測試是件很枯燥無趣的事情,而一遍又一遍的測試則更是讓人生畏的工作。幸運(yùn)的是,單元測試工具 JUnit 使這一切變得簡單藝術(shù)起來。

          JUnit Java 社區(qū)中知名度最高的單元測試工具。它誕生于 1997 年,由 Erich Gamma Kent Beck 共同開發(fā)完成。其中 Erich Gamma 是經(jīng)典著作《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書的作者之一,并在 Eclipse 中有很大的貢獻(xiàn); Kent Beck 則是一位極限編程( XP )方面的專家和先驅(qū)。

          麻雀雖小,五臟俱全。 JUnit 設(shè)計(jì)的非常小巧,但是功能卻非常強(qiáng)大。 Martin Fowler 如此評價(jià) JUnit :在軟件開發(fā)領(lǐng)域,從來就沒有如此少的代碼起到了如此重要的作用。它大大簡化了開發(fā)人員執(zhí)行單元測試的難度,特別是 JUnit 4 使用 Java 5 中的注解( annotation )使測試變得更加簡單。

          2. 概念介紹

          2.1 簡單測試

          在附件中的代碼包中有生產(chǎn)代碼 Money 類。請看以下對 Money 類的測試代碼(所有測試代碼也可以在附件的代碼包中查找)

           1 import static org.junit.Assert.assertTrue ;
           2 import org.junit.Test;
           3 
           4 public class TestSample { 
           5 
           6          @Test
           7          public void add() {
           8                    Money m12CHF = new Money(12"CHF" );
           9 
          10                    Money m14CHF = new Money(14"CHF" );
          11 
          12                    Money expected = new Money(26"CHF" );
          13 
          14                    Money result = m12CHF.add(m14CHF);
          15 
          16                    assertTrue (expected.equals(result));
          17          }
          18 }
          19 

          在這段測試代碼中有幾點(diǎn)需要注意和 Junit3 測試代碼不同之處:

          1 該測試方法主要測試 Money 類中 add ()方法是否能正確對貨幣進(jìn)行操作。在 Junit4 中對測試方法只要用 @Test 就標(biāo)明該方法是測試方法。方法名字可以自行定義,不像 Junit3 中所有測試方法都要有 test 前綴。

          2 測試類也不需要繼承 TestCase Junit4 中測試類不需要繼承任何 Junit 中接口和類。

          3 斷言在 Junit4 中要靜態(tài)導(dǎo)入。如本段代碼中的 assertTrue

                      Junit4 特性測試

          請先閱讀一遍下列代碼,然后我會對這段代碼的一些 Junit4 特性進(jìn)行詳細(xì)說明。


           1 import static org.junit.Assert.assertTrue ;
           2 import java.util.ArrayList;
           3 import org.junit.After;
           4 import org.junit.Before;
           5 import org.junit.Ignore;
           6 import org.junit.Test; 
           7 
           8 public class TestFixture {
           9          private Money f12CHF ;
          10          private Money f14CHF ;
          11          private Money f26CHF ;
          12          private Money f28USD ; 
          13 
          14          @Before
          15          public void setUp() {
          16                    f12CHF = new Money(12"CHF" );
          17                    f14CHF = new Money(14"CHF" );
          18                    f26CHF = new Money(26"CHF" );
          19                    f28USD = new Money(28"USD" );
          20          }
          21 
          22  
          23 
          24          @Test
          25          public void testAdd() {
          26                    Money result = f12CHF .add(f14CHF );
          27                    assertTrue (f26CHF .equals(result));
          28          }
          29 
          30  
          31          @Ignore ("this test method isn't working now" )
          32          @Test (expected = IndexOutOfBoundsException.class )
          33          public void empty() {
          34                    new ArrayList<Object>().get(0);
          35          }
          36  
          37 
          38          @After
          39          public void tearDown() {
          40                    f12CHF = null ;
          41                    f14CHF = null ;
          42                    f26CHF = null ;
          43                    f28USD = null ;
          44          }
          45 
          46  
          47          @Test (timeout = 1000)
          48          public void testTimeOut() throws InterruptedException {
          49                    //                 wait(100);
          50                    Money result = f12CHF .add(f14CHF );
          51          }
          52 }

          1 @Before @After

          Junit3 中對測試數(shù)據(jù)的初始化是放在 setUp 方法中,而對測試數(shù)據(jù)的重置和銷毀是放在 tearDown 方法中。在 Junit4 中則可以使用 @Before @After 來代替。在這段代碼中方法名字還是使用 setUp tearDown 是為了讓有 Junit3 經(jīng)驗(yàn)的開發(fā)者更好理解 @Before @After 。其實(shí)在 Junit4 中這兩個(gè)方法名可以自行定義為其他任何名字。

          2 @Test expected=XXXException.class

          JUnit4.0 之前,對錯(cuò)誤的測試,我們只能通過 fail 來產(chǎn)生一個(gè)錯(cuò)誤,并在 try 塊里面 assertTrue true )來測試。現(xiàn)在,通過 @Test 中的 expected 屬性就可以實(shí)現(xiàn)了。 expected 屬性的值是一個(gè)異常的類型,如代碼中的 IndexOutOfBoundsException.class (注釋掉 @lgnore(“this test method isn't working now ”) ,運(yùn)行后可查看

          3 @Ignore

          有該標(biāo)記的測試方法在測試中會被忽略。,你可以為該標(biāo)簽傳遞一個(gè) String 的參數(shù),來表明為什么會忽略這個(gè)測試方法。比如: @lgnore(“this test method isn't working now ”) ,在執(zhí)行的時(shí)候,僅會報(bào)告該方法沒有實(shí)現(xiàn),而不會運(yùn)行該測試方法。如代碼中的 empty ()方法。

          4. @ Test timeout =…

          標(biāo)記 傳入了一個(gè)時(shí)間(毫秒)給測試方法,如果測試方法在指定的時(shí)間之內(nèi)沒有運(yùn)行完,則測試會失敗,即使被測試方法測試正確也會失敗。該標(biāo)記主要用于測試被測試方法運(yùn)行所需時(shí)間,即用于方法的簡單性能測試。(將 wait(100);注釋后運(yùn)行查看結(jié)果,再取消注釋運(yùn)行查看結(jié)果,對比兩種結(jié)果

          再看下列代碼。


           1 import static org.junit.Assert.assertTrue ;
           2 import org.junit.AfterClass;
           3 import org.junit.BeforeClass;
           4 import org.junit.Test;
           5 
           6 public class TestOnce {
           7          private static Money f12CHF ;
           8          private static Money f14CHF ;
           9          private static Money f26CHF ;
          10          private static Money f28USD ;
          11  
          12 
          13          @BeforeClass
          14          public static void setUp() {
          15                    f12CHF = new Money(12"CHF" );
          16                    f14CHF = new Money(14"CHF" );
          17                    f26CHF = new Money(26"CHF" );
          18                    f28USD = new Money(28"USD" );
          19          }
          20  
          21          @Test
          22          public void testAdd() {
          23                    Money result = f12CHF .add(f14CHF );
          24                    assertTrue (f26CHF .equals(result));
          25          }
          26  
          27          @AfterClass
          28          public static void TearDown() {
          29                    f12CHF = null ;
          30                    f14CHF = null ;
          31                    f26CHF = null ;
          32                    f28USD = null ;
          33          }
          34 }

          5 @BeforeClass @AfterClass

          這里 @BeforeClass @AfterClass 是為了在一個(gè) Test 類的所有測試方法執(zhí)行前后各執(zhí)行一次。這是為了能在 @BeforeClass 中初始化一些昂貴的資源,例如數(shù)據(jù)庫連接,然后執(zhí)行所有的測試方法,最后在 @AfterClass 中釋放資源。

          正如你看到的,由于 @BeforeClass @AfterClass 僅執(zhí)行一次,因此它們只能標(biāo)記靜態(tài)方法,在所有測試方法中共享的資源也必須是靜態(tài)引用。可仔細(xì)查看上述代碼中對私有成員變量和標(biāo)記 @BeforeClass @AfterClass 的方法的類型。

                      Junit4 參數(shù)測試

          有時(shí)候要對某一特定方法傳入各種不同的參數(shù)值,用來測試這個(gè)方法的健壯性。在 Junit3 中必須為每種參數(shù)值單獨(dú)寫一個(gè)獨(dú)立的測試方法。這樣就造成很多測試代碼測試的都是同一方法,只是傳入的參數(shù)值有不同。在 Junit4 中只需要一個(gè)測試方法就能針對 N 種不同參數(shù)值進(jìn)行 N 次測試。試看如下代碼:


           1 import static org.junit.Assert.assertTrue ;
           2 import java.util.Arrays;
           3 import java.util.Collection;
           4 import org.junit.BeforeClass;
           5 import org.junit.Test;
           6 import org.junit.runner.RunWith;
           7 import org.junit.runners.Parameterized;
           8 import org.junit.runners.Parameterized.Parameters;
           9  
          10 /**
          11   * @author Administrator
          12   * 第一個(gè)注意點(diǎn)
          13   */
          14 @RunWith (Parameterized.class )
          15 public class TestParameter {
          16          private static Money f12CHF ;
          17          //       第二個(gè)注意點(diǎn)
          18          private Money expected ;
          19          private Money target ;
          20  
          21          @BeforeClass
          22          public static void setUp() {
          23                    f12CHF = new Money(12"CHF" );
          24          }
          25  
          26          /**
          27            *  第三個(gè)注意點(diǎn)
          28            * @return
          29            */
          30          @Parameters
          31          public static Collection words() {
          32                    return Arrays.asList (new Object[][] { { new Money(23"CHF" ), new Money(11"CHF" ) },
          33                                      { new Money(28"CHF" ), new Money(16"CHF" ) }
          34                    });
          35          }
          36 
          37          /**
          38            * 第四個(gè)注意點(diǎn)
          39          * 參數(shù)化測試必須的構(gòu)造函數(shù)
          40          * @paramexpected    期望的測試結(jié)果,對應(yīng)參數(shù)集中的第一個(gè)參數(shù)
          41          * @paramtarget 測試數(shù)據(jù),對應(yīng)參數(shù)集中的第二個(gè)參數(shù)
          42          */
          43          public TestParameter(Money expected, Money target) {
          44                    this .expected = expected;
          45                    this .target = target;
          46          }
          47  
          48          /**
          49            * 實(shí)際需要測試的方法
          50            */
          51          @Test
          52          public void add() {
          53                    assertTrue (expected .equals(f12CHF .add(target )));
          54          }
          55 }

          請?jiān)敿?xì)查看注釋中所標(biāo)注的注意點(diǎn)。首先測試類需要聲明 @RunWith(Parameterized.class) 。然后設(shè)置兩個(gè)成員變量,一個(gè)是測試期望返回的值變量,如代碼中 expected 還有一個(gè)是測試方法傳入的參數(shù)值變量,如代碼中 target 。針對這兩個(gè)成員變量,新建包含這兩個(gè)成員變量的測試類構(gòu)造方法。再新建一個(gè)方法進(jìn)行參數(shù)初始化。必須將該方法聲明為 static 并返回一個(gè) Collection 類型。需要用 @Parameters 注解來修飾該方法。在該方法內(nèi)部,僅僅創(chuàng)建一個(gè)多維 Object 數(shù)組,并將該數(shù)組轉(zhuǎn)換為 List 。然后運(yùn)行 Junit 后,測試通過后的界面如下圖:

          由圖可知執(zhí)行該測試類時(shí),通過 add () 測試方法運(yùn)行兩次,將代碼中 words 方法里的每個(gè)值對運(yùn)行一次。

                      Junit4 套件測試

          相比 Junit3 的套件測試, Junit4 的套件測試只需要兩個(gè)注解就可以運(yùn)行了,而不需要寫任何代碼。代碼示例如下:


           1 import junit.framework.JUnit4TestAdapter;
           2 import junit.framework.Test;
           3 import org.junit.runner.RunWith;
           4 import org.junit.runners.Suite;
           5 import org.junit.runners.Suite.SuiteClasses;
           6 
           7 @RunWith (Suite.class )
           8 @SuiteClasses ( { TestSample.class , TestFixture.class , TestOnce.class , TestParameter.class })
           9 public class TestSuite {
          10          public static Test suite() {
          11                    return new JUnit4TestAdapter(TestSuite.class );
          12          }
          13 }

          JUnit 4 中,套件被兩個(gè)新注解所替代。第一個(gè)是 @RunWith ,設(shè)計(jì)它是為了方便讓不同的運(yùn)行器執(zhí)行一個(gè)特別的測試類。 JUnit4 綁定一個(gè)叫做 Suite 的套件運(yùn)行器,必須在 @RunWith 注釋中指定這個(gè)運(yùn)行器。不僅如此,還必須提供另一項(xiàng)叫做 @SuiteClasses 的注釋,它將一個(gè)意欲表示測試套件的類列表作為參數(shù)。如代碼所示,將需要測試的測試類依次放入 SuiteClasses 中就可以了。另外如果要使用ant1.7 之前的版本,則要像代碼中所示對suite 方法進(jìn)行編寫。如果是1.7 后版本或者是使用maven 則刪除suite 方法就可以了。

          posted on 2010-11-10 09:47 吳峻申 閱讀(177) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
           
          Copyright © 吳峻申 Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 吉安市| 无锡市| 乌海市| 江达县| 灵山县| 澳门| 富裕县| 丹阳市| 津南区| 彰武县| 睢宁县| 霸州市| 裕民县| 屏东市| 东乡族自治县| 新丰县| 高青县| 淮安市| 科尔| 定日县| 南宁市| 乃东县| 延津县| 基隆市| 黄平县| 亳州市| 渭源县| 大荔县| 阜新市| 汉阴县| 铁岭市| 海口市| 邯郸县| 将乐县| 湄潭县| 乌兰县| 湟中县| 漾濞| 宝坻区| 南宫市| 常州市|