使用本地线程的单元测试方法

时间:2010-12-02 22:52:15

标签: c# multithreading unit-testing

我有一个看起来像这样的方法:

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 个答案:

答案 0 :(得分: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表示事件已设置,或者如果为true表示超时已过期,我不记得我的头顶。检查文档。)

您可以在此处使用多个同步原语,其中一个取决于您要执行的操作。 ManualResetEvent通常对我很有用。

答案 1 :(得分:1)

还有另一种做事方式:

首先,在测试类中有一个AutoResetEvent(或者你喜欢的ManualResetEvent)。

在您的测试方法中:

//set up stuff

testEvent.WaitOne();

//ensure everything works

在你的回调中

testEvent.Set();

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

据推测,你也希望在等待电话上有某种超时。