Testing a class for making asynchronous calls

In my last post I looked at doing all kinds of evil things to get some legacy code under test. The code in question launched a new thread to do some work, and I ended up wrapping this asynchronous call in a ThreadDispatcher class for testing.

public class ThreadDispatcher {
  public AsyncCall Dispach(Action function) {
    var thread = new Thread(() => function());
    thread.Start();
    return new AsyncCall(thread);
  }
}

public class AsyncCall {
  private readonly Thread _thread;
  public AsyncCall(Thread thread) { _thread = thread; }
  public void WaitUntilCallFinishes() { _thread.Join(); }
}

I could then test the dispatcher like this:

[Test]
public void RunsFunctionFromDifferentThread() {
  var dispatcher = new ThreadDispatcher();
  int callingThreadId = Thread.CurrentThread.ManagedThreadId;
  int dispatchedThreadId = callingThreadId;
  var asyncCall = dispatcher.Dispach(() => dispatchedThreadId = Thread.CurrentThread.ManagedThreadId);
  asyncCall.WaitUntilCallFinishes();
  Assert.That(dispatchedThreadId, Is.Not.EqualTo(callingThreadId));
}

There’s (at least) two problems with this. First, the return value of this function is a complete contrivance for the sake of testability (so our tests can call asyncCall.WaitUntilCallFinishes() before checking any assertions). This isn’t always a bad thing, but in this case we are adding functionality and complexity that the production code does not need (the fact that the return value is always ignored in production code doesn’t seem very reassuring). The second problem is that if we want to use the ThreadPool to run the work in the background then we don’t get a reference to a Thread, and therefore can’t wait on it.

A neater approach

This week I was faced with an actual need to do something like this (the previous implementation was an exercise stemming from a discussion with a mate). I ended up using similar TDD steps to come up with what I feel is a nicer way of testing a fire-and-forget asynchronous call. The implementation itself is trivial, and removes all traces of waiting on/joining threads:

public class AsynchronousDispatcher {
    public void Execute(Action action) {
        ThreadPool.QueueUserWorkItem(state => action());
    }
}

The fun part is the tests. Seeing as we’ve removed the concept of waiting from our implementation, this has to be handled from our tests instead. Which is fine, because the tests are the only place we need this functionality.

[Test]
public void ShouldExecuteAction() {
    var wasRun = false;
    DispatchToThreadAndWaitUntilDone(() => wasRun = true);
    Assert.That(wasRun, "Expected action to execute");
}

[Test]
public void ShouldExecuteActionOnDifferentThread() {
    var currentThreadID = Thread.CurrentThread.ManagedThreadId;
    var dispatchedThreadID = currentThreadID;

    DispatchToThreadAndWaitUntilDone(() => dispatchedThreadID = Thread.CurrentThread.ManagedThreadId);
    Assert.That(dispatchedThreadID, Is.Not.EqualTo(currentThreadID));
}

private void DispatchToThreadAndWaitUntilDone(Action action) {
    var resetEvent = new AutoResetEvent(false);
    var dispatcher = new AsynchronousDispatcher();
    dispatcher.Execute(() =>
                           {
                               action();
                               resetEvent.Set();
                           });
    resetEvent.WaitOne();
}

You’ll see that the tests themselves are almost identical to the ones from last time, as most of the hard work is done in the DispatchToThreadAndWaitUntilDone(Action action) function. This uses an AutoResetEvent to handle synchronisation, and signals (resetEvent.Set()) as part of the work being done on our new thread. While the new thread is busy doing its asynchronous thang, the thread running our test uses WaitOne() to patiently sit around until it gets signaled that the asynchronous thread is done. We can then check that the results of the asynchronous operation matched what we expected, which for these tests is just that the code was run and that it used a different thread to run it.

So now we’ve got a trivial and tested implementation we can use to make asynchronous calls. If we extract an interface (or make Execute(...) virtual) we can easily mock this or replace it with a synchronous implementation to test classes that need to use AsynchronousDispatcher.

Apologies if this is all a bit simplistic, but I thought the tests looked kinda neat so I thought I’d share. Feel free to rip gigantic holes in it all. :)

Comments