并行運行單元測試的啟示

發表于:2015-03-27來源:uml.org.cn作者:捷道點擊數: 標簽:單元測試
我們希望采用并行的方式在本地運行單元測試,從而減少測試時間,提高開發人員的工作效率。我們使用了線程池來提供多線程的并行任務。通過配置啟動多個線程,并以程序集為單位

  我們希望采用并行的方式在本地運行單元測試,從而減少測試時間,提高開發人員的工作效率。我們使用了線程池來提供多線程的并行任務。通過配置啟動多個線程,并以程序集為單位,啟動TestRunner:

  var executorWrapper=newExcetorWrapper(assemblyName,null,false);

  var testRunner=newTestRunner(executorWrapper,newRunnerLoggerWrapper());

  testRunner.RunAssembly();

  其中的RunnerLoggerWrapper是一個自定義的類,實現了Xunit的IRunnerLogger。XUnit的使用并非本文描述的內容,在此略過。

  因為是以程序集為單位,所以我們在啟動多線程之前,會事先將需要運行的程序集放到一個隊列中,然后在啟動多線程之后,執行出隊列操作。多線程的運行代碼如下所示:

  privatestaticManualResetEvent[] resetEvents;

  privatestaticQueueassemblyQue;

  privatestaticreadonlyObject LockAssembly2Queue=newObject();

  publicvoidRun()

  {

  for(var index=0; index

  {

  resetEvents[index]=newManualResetEvent(false);

  ThreadPool.QueueUserWorkItem(DoWork, index);

  }

  WaitForAllManualEvent();

  }

  privatevoidWaitForAllManualEvent()

  {

  if(Thread.CurrentThread.ApartmentState=ApartmentState.STA)

  {

  foreach(var manualResetEventinresetEvents)

  {

  WaitHandle.WaitAny(newWaitHandle[]{manualResetEvent});

  }

  }

  else

  {

  WaitHandle.WaitAll(resetEvents);

  }

  }

  privatestaticvoidDoWork(Object index)

  {

  Thread.CurrentThread.ApartmentState=ApartmentState.STA;

  while(true)

  {

  stringcurrentAssemblyName=null;

  lock(LockAssembly2Queue)

  {

  if(assemblyQue.Count!=0)

  {

  currentAssemblyName=assemblyQue.Dequeue();

  }

  else

  {

  resetEvents[(int)index].Set();

  Console.WriteLine("Exited current thread:{0}", Thread.CurrentThread.Name);

  break;

  }

  }

  if(currentAssemblyName!=null)

  {

  newTestRunnerWrapperWithAssembly(currentAssemblyName).Runner();

  }

  }

  }

  由于要測試的程序集比較多,采用這種并行方式可以極大地提高運行效率。由于單元測試彼此是獨立的,在并行運行時,互相沒有干擾。這是我們實現判斷的結果。一切看起來很美好,但在真正運行時,卻出現了大量的死鎖。異常信息為:

  Transaction (Process ID) was deadlocked on resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

  在我們的單元測試中,大多數測試需要訪問的資源都是在內存中進行,但有一部分單元測試必須與數據庫通信,對數據表進行讀寫。除了極少數特殊的測試用例外,對數據表的操作都放在事務中進行,并在執行完畢后,通過回滾事務,避免對真實數據的提交,保證單元測試不會影響數據庫。

  注:單元測試應該訪問數據庫嗎?這其實還有待確認。在《修改代碼的藝術》一書中,Feathers這樣寫道:

  單元測試運行得快。運行得不快的不是單元測試。

  有些測試容易跟單元測試混淆起來。譬如下面這些測試就不是單元測試:

  (1)跟數據庫有交互;

  (2)進行了網絡間通信;

  (3)調用了文件系統;

  (4)需要你對環境作特定的準備(如編輯配置文件)才能運行的。

  以上可以看到Feathers的態度是單元測試不應與外部資源進行交互。顯然,如果出現了這些交互,就應該采用Mock的方式來模擬對外部資源的訪問。然而,某些實現功能卻是與外部資源息息相關,又或者我們測試的目的本身就是驗證對外部資源的訪問是否正確。從測試的范圍來看,它們仍然算是單元測試,但因其特殊性,而應該將這些測試放到系統測試的范疇。在持續集成中,我們常常用金字塔來表示單元測試、系統測試和集成測試的數量。如下圖所示:

  單元測試的數量最多,如果還需要訪問外部資源,就會嚴重影響運行單元測試的速度。關于單元測試、Mock等內容,我希望在以后的文章里詳細論述。

原文轉自:http://www.uml.org.cn/Test/201204112.asp

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97