lexy

          支持開源,尊重他人的勞動(dòng)!

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            17 隨筆 :: 0 文章 :: 9 評論 :: 0 Trackbacks

          JUnit使用經(jīng)驗(yàn) <轉(zhuǎn)載>
                                                
          原作:Andy Schneider Richard Dallaway 等
           編譯:PMT 測試工作組
           --------------------------------------------------------------------------------

           譯者注:
               很多測試人員都有過編寫測試框架的經(jīng)歷,JUnit的出現(xiàn)避免了其中的大量重復(fù)勞動(dòng)。但如同其他的工具一樣用得好和用得差的結(jié)果是截然不同的。我們編輯這樣一個(gè)JUnit的系列希望能夠幫助越來越多的JUnit使用者用好JUnit。我們也希望讀者們能夠把自己的一些經(jīng)驗(yàn)所得和大家分享。

           經(jīng)驗(yàn)一、不要在測試用例的構(gòu)造函數(shù)中做初始化
               答案是重載測試用例的setUp()方法進(jìn)行初始化。

           經(jīng)驗(yàn)二、不要假定測試用例中測試的執(zhí)行次序
               好的習(xí)慣是保持測試之間的獨(dú)立性,使得它們在任何次序下執(zhí)行的結(jié)果都是相同的。

           經(jīng)驗(yàn)三、測試要避免人工干預(yù)
               經(jīng)驗(yàn)二講的是不同的測試要避免相關(guān)性,而經(jīng)驗(yàn)三講的其實(shí)就是測試要避免自相關(guān)。

           經(jīng)驗(yàn)四、在子類中調(diào)用父類的setUp() 和tearDown()

           經(jīng)驗(yàn)五、不要硬性規(guī)定數(shù)據(jù)文件的路徑

           經(jīng)驗(yàn)六、把測試的代碼和被測的代碼放在同樣的目錄下

           經(jīng)驗(yàn)七、正確命名測試

           經(jīng)驗(yàn)八、書寫測試時(shí)要考慮地區(qū)和國家設(shè)置

           經(jīng)驗(yàn)九、利用Junit 的自動(dòng)異常處理書寫簡潔的測試代碼
               事實(shí)上在Junit 中使用try-catch 來捕獲異常是沒有必要的,Junit 會(huì)自動(dòng)捕獲異常。那些沒有被捕獲的異常就被當(dāng)成錯(cuò)誤處理。

           經(jīng)驗(yàn)十、充分利用Junit 的assert/fail 方法
               assertSame()用來測試兩個(gè)引用是否指向同一個(gè)對象
               assertEquals()用來測試兩個(gè)對象是否相等

           經(jīng)驗(yàn)十一、確保測試代碼與時(shí)間無關(guān)

           經(jīng)驗(yàn)十二、使用文檔生成器做測試文檔

           

          經(jīng)驗(yàn)一、不要在測試用例的構(gòu)造函數(shù)中做初始化
          當(dāng)我們需要增加一個(gè)測試時(shí),我們要書寫一個(gè)自己的測試用例,比如SomeTest。如果你喜歡在SomeTest的
          構(gòu)造函數(shù)中做有關(guān)的初始化工作,這可不是個(gè)好習(xí)慣。如下例:


          public Class SomeTestclass SomeTest extends TestCase{
              public SomeTest(String testName){
                  super(testName);
                  //初始化代碼
              }
          }
          一旦初始化代碼產(chǎn)生異常,比如IllegalStateException,JUnit隨之將產(chǎn)生一個(gè)AssertionFailedError,
          并顯示類似下面的出錯(cuò)信息:
          junit.framework.AssertionFailedError:Cannotinstantiatetestcase:test1at
          junit.framework.Assert.fail(Assert.java:143at
          junit.framework.TestSuite$1.runTest(TestSuite.java:178at
          junit.framework.TestCase.runBare(TestCase.java:129at
          junit.framework.TestResult$1.protect(TestResult.java:100at
          junit.framework.TestResult.runProtected(TestResult.java:117at
          junit.framework.TestResult.run(TestResult.java:103at
          junit.framework.TestCase.run(TestCase.java:120at
          junit.framework.TestSuite.run(TestSuite.java,CompiledCodeat
          junit.ui.TestRunner$12.run(TestRunner.java:429
          這一大堆出錯(cuò)信息只會(huì)讓人一頭霧水,我們只知道JUnit無法實(shí)例化某個(gè)測試用例,到底出了什么問題,在
          哪兒出錯(cuò)了呢?不知道!
          那么好的做法是怎樣呢?
          答案是重載測試用例的setUp()方法進(jìn)行初始化。當(dāng)setUp()中的初始化代碼產(chǎn)生異常時(shí)我們得到的
          是類似下面的出錯(cuò)信息:
          java.lang.IllegalStateException:Oopsatbp.DTC.setUp(DTC.java:34at
          junit.framework.TestCase.runBare(TestCase.java:127at
          junit.framework.TestResult$1.protect(TestResult.java:100at
          junit.framework.TestResult.runProtected(TestResult.java:117at
          junit.framework.TestResult.run(TestResult.java:103
          ...
          顯然這要清楚得多我們一下子就可以知道是在DTC.java 的第34 行產(chǎn)生了IllegalStateException

           

          經(jīng)驗(yàn)二、不要假定測試用例中測試的執(zhí)行次序
          我們知道在一個(gè)JUnit 的測試用例類中可以包含多個(gè)測試,每個(gè)測試其實(shí)就是一個(gè)method。在下面的例子
          中有兩個(gè)不同的測試,盡管testDoThisFirst()在位置上先于testDoThisSecond(),但我們不能就此假定
          testDoThisFirst()會(huì)先執(zhí)行。


          public Class SomeTestCaseclass SomeTestCase extends TestCase{
              public SomeTestCase(String testName){
                  super(testName);
              }
              public void testDoThisFirst(){
                 
              }
              public void testDoThisSecond(){
              }
          }由于JUnit 內(nèi)部使用一個(gè)Vector 來存儲所有的test,因此在不同的操作系統(tǒng)和Java 虛擬機(jī)上,test 的執(zhí)行
          次序是不可預(yù)測的。
          好的習(xí)慣是保持測試之間的獨(dú)立性,使得它們在任何次序下執(zhí)行的結(jié)果都是相同的。如果真得需要某些測試
          按照特定的次序執(zhí)行,我們可以借助addTest 來實(shí)現(xiàn)。如下例:


          public static Testsuite(){
              suite.addTest(new SomeTestCase(“testDoThisFirst”);
              suite.addTest(new SomeTestCase(“testDoThisSecond”);
              return suite;
          }
          這樣我們可以確保JUnit先執(zhí)行testDoThisFirst(),然后執(zhí)行testDoThisSecond()。

          經(jīng)驗(yàn)三、測試要避免人工干預(yù)
          如果某段測試代碼需要人工干預(yù),那至少有兩個(gè)不良后果:一則不能被包括在自動(dòng)測試中,比如夜間的回
          歸測試;二則不能被重復(fù)執(zhí)行,例如數(shù)據(jù)刪除的測試不能做完刪除就萬事大吉,比較好的做法是自動(dòng)補(bǔ)上
          刪除掉的數(shù)據(jù)。經(jīng)驗(yàn)二講的是不同的測試要避免相關(guān)性,而經(jīng)驗(yàn)三講的其實(shí)就是測試要避免自相關(guān)。

          經(jīng)驗(yàn)四、在子類中調(diào)用父類的setUp() 和tearDown()讓我們看一看下面的代碼

          public Class SomeTestCaseclass SomeTestCase extends AnotherTestCase {
              // A connection to a database
              private Database theDatabase;
              public SomeTestCase (String testName) {
                  super (testName);
              }
              public void testFeatureX () {
                 
              }
              public void setUp () {
                  // Clear out the database
                  theDatabase.clear ();
              }
          }你發(fā)現(xiàn)其中的錯(cuò)誤了嗎?setUp()應(yīng)該調(diào)用super.setUp() 以確保AnotherTestCase 中定義的環(huán)境被初
          始化了。當(dāng)然這也有例外,就是基類可以處理任意的測試數(shù)據(jù)。


           經(jīng)驗(yàn)五、不要硬性規(guī)定數(shù)據(jù)文件的路徑
          我們經(jīng)常需要從文件系統(tǒng)中讀取測試數(shù)據(jù),看下面的代碼:

          public void setUp () {
              FileInputStream inp ("C:\TestData\dataSet1.dat";
             
          }
          這段代碼需要把測試數(shù)據(jù)文件dataSet1.dat 放在C:TestData,這是有問題的。
          第一,C 盤可能沒有磁盤空間了測試人員不得不把數(shù)據(jù)文件放到其他路徑;
          第二,可能需要在其他操作系統(tǒng)比如Linux 上執(zhí)行這一測試。
          所以,一個(gè)較好的替代方案是

          public void setUp () {
              FileInputStream inp ("dataSet1.dat";
             
          }

          但事實(shí)上這樣仍不是很好,因?yàn)檫@要求數(shù)據(jù)文件的路徑和測試執(zhí)行的路徑必須是同一個(gè),如果幾個(gè)不同
          的測試都這樣的話,那要把這些測試集合起來執(zhí)行就有些困難,我們不得不頻繁的改變當(dāng)前路徑。
          為了解決這個(gè)問題,我們可以使用Class.getResource()或者Class.getResourceAsStream(),這樣我
          們可以把數(shù)據(jù)文件放在這個(gè)Class 的某個(gè)相對路徑上。
          數(shù)據(jù)文件應(yīng)該盡可能和源代碼一起都放在配置管理系統(tǒng)上,但這樣一來如果我們采用上面的Resource 機(jī)
          制,我們就需要做一件工作,就是把數(shù)據(jù)文件從原來的位置-就是源代碼的某個(gè)相對路徑,拷貝到編譯后
          的位置,也就是class 文件的相應(yīng)的相對路徑。這其實(shí)并不復(fù)雜,因?yàn)閺腸lass 的package 就可以映射到
          java文件的所在路徑對于Linux或者Windows我們所要做的就是把package中的. 用
          File.separatorChar 替代。

           

          經(jīng)驗(yàn)六、把測試的代碼和被測的代碼放在同樣的目錄下
          當(dāng)我們把測試代碼和被測的代碼放在同一目錄下時(shí),我們就可以在編譯被測代碼的同時(shí)編譯測試代碼,從
          而確保兩者是同步更新的。事實(shí)上當(dāng)前的普遍做法,就是把單元測試視為Build 的一個(gè)環(huán)節(jié)。

          經(jīng)驗(yàn)七、正確命名測試
          把測試用例命名為TestClassUnderTest,比如如果被測的Class 是MessageLog,那么測試用例就叫
          TestMessageLog,這樣做使得測試用例和被測的Class一一對應(yīng),而在測試用例中每個(gè)測試的method 就
          可以命名為
          testLoggingEmptyMessage()
          testLoggingNullMessage()
          testLoggingWarningMessage()
          testLoggingErrorMessage()
          同樣是為了說清楚測試的是什么。正確的命名可以幫助測試代碼的閱讀者了解每個(gè)測試的目的。


           
          經(jīng)驗(yàn)八、書寫測試時(shí)要考慮地區(qū)和國家設(shè)置
          比如某個(gè)測試要使用日期,下面的代碼就是創(chuàng)建日期對象的一種方法
          Date date = DateFormat.getInstance ().parse ("dd/mm/yyyy";
          但是如果運(yùn)行上面測試代碼的機(jī)器采用不同的地區(qū)國家設(shè)置,那么就會(huì)有問題。因此我們最好用下面的另
          一種方法:
          Calendar cal = Calendar.getInstance ();
          Cal.set (yyyy, mm-1, dd);
          Date date = Calendar.getTime ();
          顯然,第二種方法能夠適應(yīng)地區(qū)國家設(shè)置的變化。


           
          經(jīng)驗(yàn)九、利用Junit 的自動(dòng)異常處理書寫簡潔的測試代碼
          很多Junit 的初學(xué)者經(jīng)常會(huì)寫出類似下面的這些代碼


          public void exampleTest () {
              try {
                  // do some test
              } catch (SomeApplicationException e) {
                  fail ("Caught SomeApplicationException exception";
              }
          }
          事實(shí)上在Junit 中使用try-catch 來捕獲異常是沒有必要的,Junit 會(huì)自動(dòng)捕獲異常。那些沒有被捕獲的異常就
          被當(dāng)成錯(cuò)誤處理。所以上面的代碼很冗余,完全可以寫成下面等效卻簡潔得多的代碼:

          public void exampleTest () throws SomeApplicationException {
              // do some test
          }
          更少的測試代碼也更容易讀懂更容易維護(hù)。

           


          經(jīng)驗(yàn)十、充分利用Junit 的assert/fail 方法
          Junit 有豐富而靈活的assert/fail 方法,如何用好這些方法也是大有講究的。比如下面的寫法就不大好
          assert (creds == 3);
          不如寫成
          assertEquals ("The number of credentials should be 3", 3, creds);
          第二種寫法不僅易于閱讀,而且在執(zhí)行時(shí)如果fail 也可以向測試人員提供更多的信息。
          Junit 也有支持浮點(diǎn)數(shù)的assert方法,干凈利索如下例
          assertEquals ("some message", result, expected, delta);
          另外要一提的是:
          assertSame()用來測試兩個(gè)引用是否指向同一個(gè)對象
          assertEquals()用來測試兩個(gè)對象是否相等


          經(jīng)驗(yàn)十一、確保測試代碼與時(shí)間無關(guān)
          盡量避免可能過期的測試數(shù)據(jù),這種數(shù)據(jù)應(yīng)該可以手工或者自動(dòng)的刷新。另外還有一個(gè)技巧就是在使用這些
          數(shù)據(jù)前更改系統(tǒng)的當(dāng)前日期,數(shù)據(jù)操作結(jié)束后再恢復(fù)日期。當(dāng)然,使用這一技巧要注意可能的副作用。

          經(jīng)驗(yàn)十二、使用文檔生成器做測試文檔
          我們當(dāng)然可以使用文本編輯器來書寫單元測試的文檔,但是更好的方法是使用文檔生成器比如JavaDoc自
          動(dòng)生成,這樣我們就不需擔(dān)心實(shí)現(xiàn)和文檔之間的同步問題。自動(dòng)生成的文檔格式統(tǒng)一錯(cuò)誤也少。

           

          posted on 2007-10-25 17:16 lexy 閱讀(1583) 評論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 唐海县| 孟村| 定陶县| 新宾| 宁强县| 永和县| 尖扎县| 辽中县| 商河县| 修文县| 铁岭市| 磐安县| 郎溪县| 凌源市| 宁夏| 东乡县| 炎陵县| 监利县| 东海县| 观塘区| 成都市| 乐陵市| 阿拉善左旗| 汉川市| 佛教| 当雄县| 巴彦县| 桂林市| 三河市| 广丰县| 额敏县| 庆云县| 天全县| 栖霞市| 迭部县| 黎城县| 寿光市| 丹阳市| 嘉定区| 阳江市| 卢氏县|