开发者

Unit testing methods with a local Thread

开发者 https://www.devze.com 2023-01-28 11:27 出处:网络
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.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消