Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用本地线程的单元测试方法_C#_Multithreading_Unit Testing - Fatal编程技术网

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

C# 使用本地线程的单元测试方法,c#,multithreading,unit-testing,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

我有一个方法如下所示:

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();
}
[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);
}
[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);
}
然后线程开始运行并执行一些逻辑(最后在测试中调用委托)

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

我可以只添加
System.Threading.Thread.Sleep(1000)并希望逻辑不会花费超过一秒钟的时间(应该不会)。但这似乎是一个黑客

问题是,我不想将该线程暴露给外部世界,甚至不想暴露给班上的其他人

在我的单元测试中,是否有一些很酷的方法可以再次找到那个线程并等待它

大概是这样的:

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();
}
[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);
}
[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);
}

很明显,我用了酷法来阅读法。但是为什么要这样做呢?

所以如果我理解这是如何工作的,那么您注册的代理就是在第二个线程中被调用的代理,对吗?在这种情况下,您可以在测试和调用的委托中使用线程同步。我总是在单元测试中做这种事情

大概是这样的:

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();
}
[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);
}
[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通常对我很有效。

还有另一种方法:

首先,在测试类中有一个自动重置事件(或者手动重置事件,如果您愿意的话)

在您的测试方法中:

//set up stuff

testEvent.WaitOne();

//ensure everything works
在你的回电中

testEvent.Set();
然后,测试方法将停止,直到调用回调为止


大概你也希望在等待调用上有某种超时。

与你问的问题没有什么关系,但是请考虑使用线程池,而不是每次启动一个新线程。线程的启动成本相对较高,如果它在一秒钟内终止,让线程池重用线程会更有效。@David Yaw-谢谢你的提示!我会调查的,这正是我需要的!非常感谢你!