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-谢谢你的提示!我会调查的,这正是我需要的!非常感谢你!