views:

192

answers:

3

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?

+1  A: 

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...

Jon Skeet
The problem is that the threads are created by the system under test. You usually don't even know them, unless you get called back. There is no general solution for background threads in unit tests.
Stefan Steinegger
Wait on an event in the test function and set the event in the async callback.
Ates Goral
+2  A: 

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.

Stefan Steinegger
A: 

I tried what Stefan suggested and the following works for me:


public void TestFooMessage() {
    Foo message = null;
    var resetEvent = new AutoResetEvent(false);

    Server.OnReceive += (s, e) => {
         message = e.Message;
         resetEvent.Set();
    };

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

    if (resetEvent.WaitOne(1000)) {
         Assert.IsInstanceOfType(e.Message, typeof(Foo));
    } else {
         Assert.Fail("Foo not received!");
    }
}

Thanks!

Rafael Romão