• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 并行運行單元測試的啟示

    發表于:2014-10-14來源: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

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>