2010-12-02 67 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(); 

您的测试方法,然后停止,直到回调函数被调用。

大概你会希望在等待呼叫时出现某种超时。