程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 擴展JUnit測試並行程序

擴展JUnit測試並行程序

編輯:關於JAVA

測試並行程序與以往有什麼不同 ?

隨著多核的普及,並行程序的開發已經提上日 程。相對串行程序而言,並行程序更有可能出錯。一方面,並行程序的執行序列具有很強的 隨機性,線程交錯執行的序列可能每次都不一樣,而只要一個序列有問題,整個程序就是不 正確的。另一方面,並行程序對大多數程序員來說,都是一個新的領域,經驗相對較少,這 是容易出錯的另外一個因素。

既然如此,我們就更需要仔細的測試我們的並行程序和 組件了。目前已經有一些 JUnit 擴展可以創建多個線程,同時運行多個測試用例,從而加快 測試用例集的執行速度,如 p-unit 。但對於測試並行程序和組件,這些功能並不能滿足所 有的需求。因為開發人員通常希望可以精確地控制多個線程之間的同步。

與測試順序 程序相同,我們希望能在測試並行操作之前,首先准備一些測試數據。然後,啟動多個線程 測試執行不同的操作。最後,等待所有線程結束之後,檢驗結果的正確性。在第二階段中, 多個線程能以任意的次序交錯執行。結果的正確性檢查應與線程的執行測序無關。

標 准 JUnit 只捕捉來自主線程的 Exception 。而其他線程中產生的 Exception 則會安靜地被 忽略掉,使得我們在子線程運行出錯的情況下仍舊能得到“ Green Bar ”。這顯 然不是程序員喜歡的測試行為,我們希望測試結果能正確地反映所有線程的運行結果。

這種用於並行程序的測試模式會在測試並行程序時會不斷的重復。如果開發人員每次 都需要重復創建這些框架,不僅繁瑣,而且容易引入錯誤。通過使用以下介紹的簡單擴展, 可以使並行程序的測試變得和順序程序一樣簡單。這種擴展並不影響 JUnit 的其他特性以及 各種 IDE 的 JUnit 插件的使用。

下載並使用擴展框架

首先,我們給出一個 使用新擴展進行並行測試的例子。

例 1. 使用 JUnit 擴展進行並行測試

/**
 * @author Zhi Gan
 *
 */
 @RunWith (Parallelized.class)
 @ParallelSetting(threadNumber = { 1, 2, 4, 8  })
 public class TestThreaded {
 Set<String> strSet;

 @Before
 public void setUp() {
  strSet = new  LockFreeSet();
 }

 @Test
 public void doNothing() {

 }

  @InitFor("testThread")
 public void  putSomeData(int size){
  strSet.add("putSomeData");
 }

  @Threadedpublic void testThread(int rank, int size) {
  //  every thread adds element to set
  strSet.add("abcde" + rank);
 }

  @CheckFor("testThread")
 public void checkResult(int  size) {
  assertEquals(size+1, strSet.size());
 }

  public static void main(String[] args) {
  for (int i = 0; i  < 10; i++)
  JUnitCore.runClasses(TestThreaded.class);
 }
 }

如果我們在 Eclipse 中運行測試,那麼測試完畢之後的 JUnit 視圖如下 所示:

圖 1. 並行測試的完成結果

接下來, 我們模擬子線程在運行時拋出異常。

例 2. 子線程運行時異常

/**
 * @author Zhi Gan
 *
 */
 @RunWith(Parallelized.class)
 @ParallelSetting(threadNumber = { 1, 2, 4, 8 })
 public  class TestThreaded {
 Set<String> strSet;

 @Before
 public void setUp() {
  strSet = new LockFreeSet();
 }

 @Test
 public void doNothing() {

 }

   @InitFor("testThread")
 public void putSomeData(int size){
   strSet.add("putSomeData");
 }

  @Threadedpublic void  testThread(int rank, int size) {
  // throw a runtime error in  spawned thread
  throw new RuntimeError();
 }

   @CheckFor("testThread")
 public void checkResult(int size) {
   assertEquals(size+1, strSet.size());
 }

 public static void  main(String[] args) {
  for (int i = 0; i < 10; i++)
  JUnitCore.runClasses(TestThreaded.class);
 }
 }

如果 我們在 Eclipse 中運行測試,那麼測試完畢之後的 JUnit 視圖如下所示:

圖 2. 並 行測試的完成結果

從上圖可 以看出,我們的並行測試用例通過了測試,並且它們在使用不同線程運行時都能正常的工作 。而串行的測試方法 (doNothing) 的執行結果則完全和之前一樣工作正常。也就是說,串行 和並行測試可以在一個測試類中同時出現。

Annotation 詳細說明

表 1. 擴展 的 annotation 說明

名稱 參數 使用對象 備注 ParallelSetting threadNum: 用於指定線程數 目。通常我們希望能使用一個數組指定多個值,這樣,我們可以了解程序是否在單線程,較 少線程,以及大量線程的情況下是否工作正常。 用於整個 TestCase 來指定測試所 用的並行設置 用於指定整個 TestCase 的並行設置 InitFor 指定此方法服務的測試方法 用於指定初始化 方法所服務的並行測試方法   Threaded 無參數,或 定義與 @Test 兼容的參數 用於一個具有兩個 int 類型的測試方法。測試過程中, 測試框架將會線程序號以及線程總數通過方法的參數傳遞進來。這有點類似於 MPI 的約定。 指明一個方法為並行測試方法,相當於 JUnit 原有的 @Test 注釋 CheckFor 一個字符串參數,用於指定需要被驗證的並行測試方 法。 一個 Threaded 修飾的方法 指明一個方法用於檢測並行執行的結果 . 我們不能在 Threaded 方法中直接檢查,因為其他線程的測試也許還沒有結束。

擴展 JUnit 的過程說明

歸功於 JUnit 的靈活的內部架 構,只要遵循 JUnit 的標准,我們就能夠輕松的擴展 JUnit 的功能。而且遵循標准還意味 著我們的擴展能無縫的利用社區對 JUnit 的廣泛支持。例如,我們沒有編寫任何 Eclipse 插件,但是我們的測試結果能自然的在 Eclipse 中通過精心設計的 GUI 進行展現。

言歸正傳,我們擴展 JUnit 的過程主要由以下過程組成:

生成 Annotation 的定義 ,包括:@Threaded, @InitFor, @Check, @ParallelSetting

生成 TestClassRunner 的子類 Parallelized 並在其中實現運行自定義測試的邏輯

生成 TestMethodRunner 的子類供 Parallelized 類使用

在實現 ThreadedMethodRunner 時,我們最開始在類 ThreadedMethodRunner 使用了 Thread 類的 setDefaultUncaughtExceptionHandler 來捕獲 異常。然後將異常封裝到主線程。而目前的版本則利用了 Executor 來運行多線程測試。由 於 JDK 中的 Future 已經提供了類似的能力,所以我們不需要再關心異常的正確傳遞問題了 。 JUnit 能准確的打印出並行測試中產生的異常信息,這也意味著我們可以使用 JUnit 提 供的 Assert 功能了。

結論

隨著多核平台逐漸成為主流,開發人員不可避免 地需要開發和測試並行應用。本文通過使用 Annotation 擴展 JUnit,使其可以更方便地支 持“准備數據——多線程運行——檢查結果”三階段的並 行測試模式,減少開發人員手工創建線程和同步的繁瑣工作。並且可以使 JUnit 支持從子線 程中捕獲測試錯誤,正確地在 Eclipse 等 IDE 中顯示測試結果。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved