2009-08-21 31 views
4

this問題繼...我試圖單元測試以下情形:是否可以在單元測試中抽出時間?

我有一個類,允許一個調用一個方法來執行一些操作,如果失敗等待一秒鐘,回憶一下,方法。

說我想調用一個方法DoSomething()...但是在DoSomething()拋出異常的情況下,我想能夠重試調用它最多3次,但等待1秒每次嘗試之間。在這種情況下,單元測試的目的是驗證當我們調用DoSomething()3次,每次重試之間等待1秒,所花費的總時間> 3秒。

不幸的是,我能想到的,以測試它的唯一方法,是用秒錶....有兩個副作用...

  1. 需要3秒,執行測試到火候。 ..我通常喜歡我的測試以毫秒運行
  2. 運行測試的時間量相差大約+/- 10毫秒左右,這可能會導致測試失敗,除非我考慮此差異。

什麼將是很好的,如果有模擬出在這個時間的依賴,使我的測試能夠運行更快,在它的結果相當一致的方式。不幸的是,我想不到這樣做...所以我想我會問,看看你們有沒有遇到過這個問題...

回答

7

您可以創建一個Waiter類,該類提供了一個等待指定時間的方法Wait(int)。對這個方法進行測試,然後在你的單元測試中,傳入一個模擬版本,只記錄要求等待的時間,但是會立即返回。

例如(C#):

interface IWaiter 
{ 
    void Wait(int milliseconds); 
} 

class Waiter : IWaiter 
{ 
    public void Wait(int milliseconds) 
    { 
     // not accurate, but for the sake of simplicity: 
     Thread.Sleep(milliseconds); 
    } 
} 

class MockedWaiter : IWaiter 
{ 
    public void Wait(int milliseconds) 
    { 
     WaitedTime += milliseconds; 
    } 
    public int WaitedTime { get; private set; } 
} 
2

我經常測試重試嘗試像這個。我使用的方法是使超時可配置 - 然後我可以在我的測試中將其設置爲零。在你的情況下,你可以嘲笑對象DoSomething()被調用,期望3次調用它並設置超時爲0秒 - 然後你可以驗證DoSomething()被直接調用3次。

另一種方法是使用一個接口ITimer,在每次調用DoSomething()之間調用Wait(int seconds) - 然後你可以模擬出ITimer並驗證Wait(int)被調用3次用秒數的正確參數。 ITimer的具體實現然後執行Thread.sleep或任何您用於等待的方法。

4

訣竅是創建時間提供的模擬。

在Ruby中,您只需使用嘲諷庫:

現在= Time.now Time.expects(:現在).returns(現在的)。一旦 Time.expects(:現在).returns(現+ 1)。僅當 等

在Java中,它有點複雜。用自己的 「時鐘」

interface Clock { 
    Date getNow(); 
} 

,然後替換常規時間,重寫:

public String elapsedTime(Date d) { 
    final Date now = new Date(); 
    final long ms = now.getTime() - d.getTime(); 
    return "" + ms/1000 + " seconds ago"; 
} 

爲:

public String elapsedTime(Date d, Clock clock) { 
    final Date now = clock.getNow(); 
    final long ms = now.getTime() - d.getTime(); 
    return "" + ms/1000 + " seconds ago"; 
} 

顯然,時鐘可以以其他方式注入,但現在我們有一個可測試和可擴展的方法。

+0

在C#3.0中,您可以用Func 替換Clock接口。 – 2009-08-21 07:15:37

+0

有一些Java模擬庫。例如EasyMock。 – 2009-08-21 09:10:08

+0

對於Java,代碼通常從新的Date()中獲取當前時間。使用EasyMock沒有幫助,因爲您無法覆蓋返回的日期。因此引入了包裝「Clock」類。對不起,我沒有解釋清楚。 – ndp 2009-09-19 03:48:45

1

你的直覺是正確的。訣竅是將問題的不同部分分開,而不是將它們捆綁到一個類中。你需要一個Action對象來實現獨立於時間的DoSomething;一個Retryer對象,它將管理嘗試調用Action對象;和一個等待的時鐘。您可以直接對Action對象進行單元測試,單元測試具有存根的Clock和Action對象的Retryer,並使時鐘如此簡單以至於它可以正常工作。

最重要的是,不要把真正的睡眠放在測試中,它太脆弱,太慢。

相關問題