其實(shí)我是一個(gè)程序員 |
|
|||
日歷
統(tǒng)計(jì)
導(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
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è)是
|
![]() |
|
Copyright © 吳峻申 | Powered by: 博客園 模板提供:滬江博客 |