国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

用JUnit框架實現Java單元測試

2019-11-18 13:06:40
字體:
來源:轉載
供稿:網友

  隨著軟件項目的逐漸增大,軟件測試在軟件開發中的地位顯得越來越重要。假如軟件項目沒有良好的測試流程,隨著系統的增大,無論項目治理人員還是軟件開發人員都會對項目的前景失去信心,甚至會對項目的目標產生分歧,因為長期以來沒有對程序代碼和系統設計進行有效的控制,很多問題都被暫時掩蓋或逐漸演化成其他的問題。軟件開發周期越長,就會使得問題進化的版本越多,最后造成的結果是“剪不斷,理還亂”。
  單元測試是整個測試流程中最基礎的部分,它們要求程序員盡可能早地發現問題,并給予控制,這是其一。另外,假如集成測試出現問題,它們可以幫助診斷。這樣就為在軟件開發流程中建立高效的事件反應機制打下了堅實基礎。
  JUnit就是為java程序開發者實現單元測試提供一種框架,使得Java單元測試更規范有效,并且更有利于測試的集成。
  JUnit的內部結構
  JUnit的軟件結構
  JUnit 共有七個包,核心的包就是junit.framework 和junit.runner。Framework包負責整個測試對象的構架,Runner負責測試驅動。
  JUnit的類結構
  JUnit有四個重要的類:TestSuite、TestCase、TestResult、TestRunner。前三個類屬于Framework包,后一個類在不同的環境下是不同的。這里使用的是文本測試環境,所以用的是 junit.textui.TestRunner。各個類的職責如下:
  1.TestResult,負責收集TestCase所執行的結果,它將結果分為兩類,客戶可猜測的Failure和沒有猜測的Error。同時負責將測試結果轉發到TestListener(該接口由TestRunner繼續)處理;
  2.TestRunner,客戶對象調用的起點,負責對整個測試流程的跟蹤。能夠顯示返回的測試結果,并且報告測試的進度。
  3.TestSuite, 負責包裝和運行所有的TestCase。
  4.TestCase, 客戶測試類所要繼續的類,負責測試時對客戶類進行初始化,以及測試方法調用。
  另外還有兩個重要的接口:Test和TestListener。
  1.Test, 包含兩個方法:run() 和countTestCases(),它是對測試動作特征的提取。
  2.TestListener, 包含四個方法:addError()、addFailure()、startTest()和endTest(),它是對測試結果的處理以及測試驅動過程的動作特征的提取。
  下面給出的兩個類圖(篇幅有限,只顯示主要部分)很好地闡明了類之間的關系,以及junit的設計目標(如圖1)。測試案例的類采用Composite模式。這樣,客戶的測試對象就轉變成一個“部分—整體”的層次結構。客戶代碼僅需要繼續類TestCase,就可以輕松的與已有的其他對象組合使用,從而使得單元測試的集成更加方便。
   用JUnit框架實現Java單元測試(圖一)
  圖1 測試結構圖
  圖2是測試跟蹤類圖。圖2左邊TestSuite包含了測試對象集合,右邊包含了測試結果集。具體如何處理結果,以及包含哪些測試對象,并沒有立即得出結論,而是盡量地延遲到具體實現的時候。例如,實現接口TestListener的JUnit中就含有:junit.awtui.TestRunner、junit.swingui. TestRunner、junit.ui.TestRunner等,甚至客戶用自己的類實現TestListener,從而達到多樣化的目的。
   用JUnit框架實現Java單元測試(圖二)
   圖2 測試跟蹤圖
  從以上兩個類圖,可以了解JUnit對單元測試的基本思路,這個框架的核心就是結果集和案例集。
  JUnit的實現流程
  典型的使用JUnit的方法就是繼續TestCase類,然后重載它的一些重要方法:setUp()、teardown()、runTest()(這些都是可選的),最后將這些客戶對象組裝到一個TestSuite對象中,交由 junit.textui.TestRunner.run (案例集) 驅動。下面分析案例集是如何運轉的。
  圖3基本上闡述JUnit的測試流程架構。我們將從不同的角度來具體分析這個圖。
  用JUnit框架實現Java單元測試(圖三)
  圖3 測試序列圖
  首先,從對象的創建上來分析。客戶類負責創建Suite和aTestRunner。注重,類TestRunner含有一個靜態函數Run(Test),它自創建本身,然后調用doRun()。客戶類調用的一般是該函數,其代碼如下:
  static public void run(Test suite)
  {
   TestRunner aTestRunner= new TestRunner();//新建測試驅動
   aTestRunner.doRun(suite, false);//用測試驅動運行測試集
  }
  Suite對象負責創建眾多的測試案例,并將它們包容到本身。客戶測試案例繼續TestCase類,它將類,而不是對象傳給Suite對象。Suite對象負責解析這些類、提取構造函數和待測試方法。以待測試方法為單位構造測試案例,測試案例的fName就是待測試方法名。測試結果集由aTestRunner創建。這似乎同先前闡述的類圖有些矛盾,那里闡述了一個測試集可以包含很多個不同的測試驅動,似乎先創建結果集比較理想。顯然,這里對測試結果的處理只采用了一種方式,所以這樣做同樣可行。
  其次,從測試動作的執行上來分析,測試真正是從suite.run(result) 開始的。其代碼如下:
  
  public void run(TestResult result)
  {
  //從案例集中獲得所有測試案例,分別執行
   for (Enumeration e= tests(); e.hasMoreElements(); )
   {
   if (result.shouldStop() )
   break;
   Test test= (Test)e.nextElement();
   runTest(test, result);
   }
  }
  一旦測試案例開始執行,首先使用一個回調策略將自身交由Result。這樣做的每一步測試,測試驅動aTest Runner都可以跟蹤處理。這無形中建立了一個龐大的監視系統,隨時都可以對所發生的事件給予不同等級的關注。
  我們分析一下涉及到的動作行為的設計模式:
  1. Template Method (模板方法)類行為模式,它的實質就是首先建立方法的骨架,而盡可能地將方法的具體實現向后推移。TestCase.runBare()就采用了這種模式,客戶類均可以重載它的三個方法,這樣使得測試的可伸縮性得到提高。
  public void runBare() throws Throwable
  {
   setUp();
   try {runTest();}
   finally {tearDown();}
  }
  2. Command (命令)對象行為模式,其實質就是將動作封裝為一個對象,而不關心動作的接收者。這樣動作的接收者可以一直到動作具體執行時才需確定。接口Test就是一個Command集,使得不同類的不同測試方法可以通過同一種接口Test構造其框架結構。這樣對測試的集成帶來了很多方便。
  JUnit的Exception的拋出機制
  JUnit的異常層次分為三層:1.Failure,客戶預知的測試失敗,可以被Assert方法檢測到;2. Error,客戶測試的意外造成的;3.Systemerror, JUnit的線程死亡級異常,這種情況一般很少發生。JUnit的這三種異常在TestResult類的RunPRotected()方法得到很好體現。這里用Protectable接口封裝了Test的執行方法,其實p.protect執行的就是test.runBare()。
  
  public void runProtected(final Test test, Protectable p)
  {
   try {p.protect();}
   catch (AssertionFailedError e) 
   {addFailure(test, e);}
   catch (ThreadDeath e)
   {rethrow e;}
   catch (Throwable e)
   {addError(test, e);}
  }
  代碼首先檢查是否是Assertion FailedError,然后判定是否是嚴重的ThreadDeath。這種異常必須Rethrow,才能保證線程真正的死亡,假如不是,說明它是一種意外。
  前兩種異常均保存在測試結果集中,等到整個測試完成,依次打印出來供客戶參考。
  實施JUnit的幾點建議
  從以上的分析中,可以了解JUnit的結構和流程,但是在實際應用JUnit時,有幾點建議還需要說明,如下:
  1. 客戶類可以重載runTest(),它的缺省實現是調用方法名為fName的測試方法。假如客戶不是使用TestSuite加載TestCase,就尤其需要對其重載,當然這種方式并不贊成使用,不利于集成。另外,setUp()和tearDown()的功能似乎與構造函數雷同,但假如測試案例之間具有類繼續關系,采用構造函數初始化一些參數就會造成數據的混亂,不利于判定測試結果的有效性。
  2. 待測試函數的調用順序是不確定的,采用的數據結構是Vector()。假如需要有順序關系,可以將它們組合到一起,然后用同一個測試方法。
  3. 為了使測試結果清楚明了,程序中最好不要有打印輸出,要么程序的打印輸出與JUnit測試的打印輸出不要用同一個數據源System.out。其實這是兩種測試習慣,直接打印輸出是較傳統的,從測試動機上考慮它也是較隨意的,并且結果需要人工觀察。假如直接打印輸出較多的話,觀察者可能無法獲得滿足的結果。
  此外,如何擴展這個測試框架呢? junit.extensions包給出了幾點提示。我們可以使用junit.extensions. ActiveTest在不同的線程中運行一個測試實例。 對于要對測試案例添加新的功能可以采用Decorator模式,可以參考junit.extensions.TestDecorator以及它的子類junit.extensions.TestSetup、junit.extensions.RepeatedTest。這些僅僅提供了一些拓寬的思路,涉及到具體測試目標,還需進一步地挖掘。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 无为县| 申扎县| 中牟县| 汉源县| 盈江县| 达州市| 南汇区| 六枝特区| 清丰县| 招远市| 鹤山市| 曲周县| 郓城县| 红河县| 冀州市| 辉县市| 伊川县| 新昌县| 瑞昌市| 苏尼特右旗| 平遥县| 东丽区| 普安县| 什邡市| 东莞市| 金门县| 保山市| 普格县| 尼玛县| 遵义市| 土默特右旗| 涞水县| 宁津县| 黑水县| 万盛区| 磐安县| 沙坪坝区| 西乌珠穆沁旗| 志丹县| 清苑县| 万荣县|