Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle exception in a background thread in a unit test?

I'm writing a unit test suite to test a TCP/IP communication library.

As I'm using BeginAcceptClient and EndAcceptClient, the messages are received in a background thread.

After a message is received, I perform some assertions on it, but if any assertion fails, the VSTestHost.exe crashes.

I googled a bit and found out its the fact the Assert exceptions are being thrown in a background thread.

EDIT: A sample code of what I am doing, just to ilustrate:


public void TestFooMessage() {
    Server.OnReceive += (s, e) => {
        Assert.IsInstanceOfType(e.Message, typeof(Foo));
    };

    var message = new Foo();
    Client.Send(message);
}

Does anyone know how to make it work as expected: Log the assertion and continues running normally?

like image 632
Rafael Romão Avatar asked Sep 28 '09 19:09

Rafael Romão


2 Answers

You should not write the Asserts in the background thread (say: the background event handler), because the test framework can not handle this. You should only gather values there. You can synchronize the main thread for instance using AutoResetEvents. Write the values into fields, assert the fields in the main thread.

If the messages are never coming in, you need a timeout.

A bit pseudo code (actually not that pseudo):

private AutoResetEvent ReceiveEvent = new AutoResetEvent(false);
private EventArgs args;
private bool ReceiveCalled = false;

// event handler with some argument
private void Receive(object sender, EventArgs args)
{
  // get some arguments from the system under test
  this.args= args;

  // set the boolean that the message came in
  ReceiveCalled = true;

  // let the main thread proceed
  ReceiveEvent.Set();
}

[TestMethod]
public void Test()
{
  // register handler
  Server.OnReceive += Receive;

  var message = new Foo();
  Client.Send(message);

  // wait one second for the messages to come in
  ReceiveEvent.WaitOne(1000);

  // check if the message has been received
  Assert.IsTrue(
    ReceiveCalled, 
    "AcceptClientReceived has not been called");

  // assert values from the message
  Assert.IsInstanceOfType(args.Message, typeof(Foo))    
}

By the way: you still can write the handler as a lambda expression and even avoid the fields by using local variables. But it could be harder to read if everything is in a single method.

like image 65
Stefan Steinegger Avatar answered Sep 21 '22 16:09

Stefan Steinegger


I suspect you'll basically need some sort of "everything okay" flag:

  1. Create this global flag, and set it to True at the start of the test
  2. Write a parallel Assert method which clears the flag and maybe sets another variable for the "reason" (or adds it to a list) then exits the thread cleanly (if possible)
  3. Make the main test thread wait for all the other threads to finish, then check the flag.

It's possible that some test frameworks have this built in, but I don't know of any offhand...

like image 24
Jon Skeet Avatar answered Sep 18 '22 16:09

Jon Skeet