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

Vac*_*ano 3 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();
}
Run Code Online (Sandbox Code Playgroud)

然后线程关闭并执行一些逻辑(并最终在我的测试中调用委托).

我的问题是我的单元测试在线程完成之前完成.所以我的测试失败了.

我可以加入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);
}
Run Code Online (Sandbox Code Playgroud)

我显然编写CoolMethodToFindMyThread方法.但是有一些原因可以做到吗?

Chr*_*res 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);
}
Run Code Online (Sandbox Code Playgroud)

在那里有一些超时是很重要的,否则如果出现问题,你的测试就会锁定并且很难调试.WaitOne将阻塞,直到事件设置或超时到期,返回值告诉您发生了什么.

(警告:我可能会向后返回值 - 如果true表示事件已设置,或者如果为true表示超时已到期,我不记得我的头顶.检查文档.)

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