2010-12-02 47 views
3

我有一個看起來像這樣的方法:單元測試方法與本地線程

protected void OnBarcodeScan(BarcodeScannerEventArgs e) 
{ 
    // We need to do this on a seperate thread so we don't block the main thread. 
    ThreadStart starter =() => SendScanMessage(e, _scanDelegates); 
    Thread scanThread = new Thread(starter); 

    scanThread.Start(); 
} 

然後線程熄滅,做一些邏輯(和結束調用在我的測試委託)。

我的問題是我的單元測試在線程完成之前完成。所以我的測試失敗了。

我可以在System.Threading.Thread.Sleep(1000);中加上,希望邏輯永遠不會超過一秒(它不應該)。但是,這似乎是一個黑客。

問題是我不想將該線程暴露給外部世界甚至是其他課程。

是否有一些很酷的方式來再次找到該線程,並在我的單元測試中等待它?

事情是這樣的:

[TestMethod] 
[HostType("Moles")] 
public void AddDelegateToScanner_ScanHappens_ScanDelegateIsCalled() 
{ 
    // Arrange 
    bool scanCalled = false; 
    MCoreDLL.GetTopWindow =() => (new IntPtr(FauxHandle)); 

    // Act 
    _scanner.AddDelegateToScanner(_formIdentity, ((evnt) => { scanCalled = true; })); 
    _scanner.SendScan(new BarcodeScannerEventArgs("12345678910")); 

    // This line is fake! 
    System.Threading.Thread.CoolMethodToFindMyThread().Join(); 

    // Assert 
    Assert.IsTrue(scanCalled); 
} 

我明明做了CoolMethodToFindMyThread方法。但是有一些爲什麼要這樣做?

+2

與您提問的問題並不完全相關,但請考慮使用ThreadPool,而不是每次都啓動一個新線程。線程啓動起來相對昂貴,如果它在一秒鐘內終止,讓線程池重新使用線程會更有效率。 – 2010-12-02 23:01:30

+0

@David Yaw - 感謝您的提示!我會研究這一點。 – Vaccano 2010-12-03 04:35:52

回答

8

所以,如果我明白這是如何工作的,那麼你註冊的代表是在第二個線程上被調用的,對吧?在這種情況下,您可以在測試中使用線程同步以及被調用的委託。我一直在單元測試中做這種事情。

事情是這樣的:

[TestMethod] 
[HostType("Moles")] 
public void AddDelegateToScanner_ScanHappens_ScanDelegateIsCalled() 
{ 
    // Arrange 
    var scanCalledEvent = new ManualResetEvent(false); 
    MCoreDLL.GetTopWindow =() => (new IntPtr(FauxHandle)); 

    // Act 
    _scanner.AddDelegateToScanner(_formIdentity, ((evnt) => { scanCalledEvent.Set(); })); 
    _scanner.SendScan(new BarcodeScannerEventArgs("12345678910")); 

    // Wait for event to fire 
    bool scanCalledInTime = scanCalledEvent.WaitOne(SOME_TIMEOUT_IN_MILLISECONDS); 

    // Assert 
    Assert.IsTrue(scanCalledInTime); 
} 

重要的是要具有一些排序中有超時的,否則一旦出了問題你的測試只是鎖定了,這就是一種很難調試。 WaitOne會阻塞,直到事件被設置或超時過期,返回值會告訴您發生了什麼。

(警告:我可以有返回值倒退 - 我不記得了我的頭頂,如果爲true則意味着該事件得到了設置,或者如果真意味着超時過期檢查文檔)

有幾個同步原語可以在這裏使用,哪一個取決於你想要做什麼。 ManualResetEvent通常對我來說工作得很好。

1

有做事情的另一種方式:

首先,有一個的AutoResetEvent(或ManualResetEvent的,如果你喜歡)在您的測試類。

在您的測試方法:

//set up stuff 

testEvent.WaitOne(); 

//ensure everything works 

在回調

testEvent.Set(); 

您的測試方法,然後停止,直到回調函數被調用。

大概你會希望在等待呼叫時出現某種超時。