Unit testing async methods, is this the best way?

I don’t understand why there are so many 3rd-party logging components. The TraceSource concept that exists in System.Diagnostics is extremely powerful and full-featured. Our internal framework at work leverages this concept heavily. We moved away from the dependency on Enterprise Library a few years ago.

So first, if you are writing your own logging routines (actual I/O) – don’t. For so, so many reasons. If you are using some 3rd-party like log4Net, Enterprise Library, or the like – take a look at TraceSource. With a pretty modest “helper” class, you can create a pretty powerful logging subsystem that takes advantage of what is already in the .NET framework. The big additional upside is that you can get rid of a dependency on an external component.

With all that said, for a personal project, I am also adopting a very similar idea. I have a LogHelper class that has several methods like LogVerbose, LogError, etc – all with many convenience overloads. Now, I can write unit tests, but there is nothing to actually check – other than “did we get an exception?”. So, my unit tests just “fire and forget”. I’m technically getting my code coverage, but I’m not actually testing the component very well.

So, I was trying to think of a way to actually test to make sure all of my convenience overloads were chaining correctly, and that messages were ACTUALLY being written to the underlying tracelisteners. I came up with the following idea:

One of the things that my LogHelper class exposes, is the current TraceSource that it is using (the name of the assembly, by default). So, since I have access to it, I thought of hooking up a “unit test tracelistener” to it that I could monitor. This tracelistener wouldn’t actually write any messages. Instead, it would just fire an event that I could listen for, to see if the message I sent in – came out the other side. Below is what I came up with:

The TraceListener:

public class UnitTestTraceListener : TraceListener
{
public event EventHandler<MessageRecievedEventArgs> MessageRecieved;

public override void TraceEvent(TraceEventCache eventCache,
string source, TraceEventType eventType, int id,
string format, params object[] args)
{
MarkMessageRecieved(id);
}

public override void Write(string message)
{
//throw new NotImplementedException();
}

public override void WriteLine(string message)
{
//throw new NotImplementedException();
}

private void MarkMessageRecieved(int id)
{
if (MessageRecieved != null)
MessageRecieved(this, new MessageRecievedEventArgs(id));
}
}

 

Example Unit Test (with some comments to help explain):

[TestMethod]
public void ExampleUnitTest()
{
// ARRANGE
Int32 messageId = 100;
Boolean messageRecieved = false;
String format = "Test message {0}";
String args = "test value";

// Create instance of the trace listener
UnitTestTraceListener traceListener = new UnitTestTraceListener();

// Add my dummy trace listener to the trace source
LogHelper.CurrentTraceSource.Listeners.Add(traceListener);

// Start listening for when this tracelistener gets
// the message we are going to send it.
traceListener.MessageRecieved +=
new EventHandler<MessageRecievedEventArgs>((sender, e) =>
{
if (e.MessageId.Equals(messageId))
{
// Raise the flag when we recieve the same "messageId"
messageRecieved = true;
}
});

// ACT - fire the message
LogHelper.LogInformation(messageId, format, args);

// Wait (it's async)
// 20 iterations x 50ms = 1 second
for (int index = 0; index < 20; index++)
{
// Did the event above fire yet?
if (messageRecieved)
{
// We got it! Exit out.
return;
}

Thread.Sleep(50);
}

// ASSERT - if we didn't find the message by now.
// The MessageRecieved event never fired.
Assert.Fail("Did not recieve message " + messageId.ToString());
}

So, the idea is that I can wire up the event in the actual unit test and raise a flag. I then use a race-condition to see if the flag got raised. There is a chance that if the system is slow, I may get a false-negative – but it’s about the best idea I had. Race conditions are generally not a good idea – but without risking a hanging unit test, is this acceptable? I don’t know.

Technically too, I believe this is a unit test and not an integration test because I could run this test with the network cable unplugged. There are no external dependencies. By me creating a dummy trace listener, this lets me test the whole round-trip of the log message, without having any external dependencies.

Is this the best way to test async routines – setting a flag and using a race-condition? Comments welcome!

Posted in Best-practices, Uncategorized, Unit Testing
One comment on “Unit testing async methods, is this the best way?
  1. […] Unit testing async methods, is this the best way? […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 2 other followers

%d bloggers like this: