I have a method that looks like this:
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();
}
开发者_开发知识库Then the thread goes off and does some logic (and ends up calling a delegate in my test).
My problem is that my unit test finishes before the thread does. So my test fails.
I can just add in System.Threading.Thread.Sleep(1000);
and hope that the logic never takes more than a second (it should not). But that seems like a hack.
The problem is that I don't want to expose that thread to the outside world or even to the rest of the class.
Is there some cool way to find that thread again and wait for it in my unit test?
Something like this:
[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);
}
I obviously made up the CoolMethodToFindMyThread method. But is there some why to do that?
So if I understand how this works, then the delegates you register are the ones being called on the second thread, right? In that case, you can use thread synchronization in your test and the delegate that gets called. I do this kind of thing in my unit tests all the time.
Something like this:
[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);
}
It's important to have some sort of timeout in there, otherwise if something goes wrong your test just locks up and that's kind of hard to debug. WaitOne will block until the event gets set or the timeout expires, the return value tells you which happened.
(WARNING: I may have the return value backwards - I don't remember off the top of my head if true means the event got set or if true means the timeout expired. Check the docs.)
There are several sync primitives you can use here, which one depends on what you want to do. ManualResetEvent usually works pretty well for me.
There's another way of doing things:
First, have an AutoResetEvent (or ManualResetEvent, if you feel like) in your test class.
In your test method:
//set up stuff
testEvent.WaitOne();
//ensure everything works
In your callback
testEvent.Set();
Your test method will then stop until the callback gets called.
Presumably you'll want some sort of timeout on that wait call as well.
精彩评论