Unit testing is good. Sure it’s no silver bullet but there is no such thing! Looking back I’m not sure how I managed to work without writing unit tests the number of time having tests has saved me from tracking down bugs isn’t worth thinking about
However everything isn’t all rosy once you start unit testing against your code, there are a number of items that can put a spanner in the works, and that’s why I have created this series of posts to try and help other people resolve some of these common issues you may find yourself up against.
- Date & Time
- External Dependencies
- Sharing Static items
- You have already been writing unit tests
- You are familiar with state based testing and behaviour based testing & when best to use either
- Are familiar with a mocking framework (these examples use Rhino Mocks).
Let’s face it once we introduce multi-threading things usually get more complicated and trying to unit test multi-threaded code is no exception! Why? Well if we think about how our code is going to be executed it will be ran on the test runner thread by NUnit, MBUnit MSTest etc… And if we have code which is going to perform work on a separate thread this causes a problem because the test runner thread will not know that it should really be waiting for work on the other thread to finish before carrying on, let’s get into some code:
So for the example I created a simple
Processor object and as simple command based interface and a concrete object:
You could imagine that this could represent something more real such as a scheduling object that hold a list of
IWorkItem’s and can make calls to them in a async manner, so now we have everything setup we can run the unit test:
Hmm… were expecting 3 but getting 0 strange, well not really let’s try to breakdown what’s happening:
So our assertion
Assert.That(sut.Result, Iz.EqualTo(3)); is being called before the work is done hence we get zero.
So how can we tell the test runner thread to wait till the work is done? Well there’s thread.Join() however we A don’t want to expose the thread object outside of the
Processor object as this breaks encapsulation and B In this case we use the threadpool so don’t actually have a new thread created.
With thread.Join() out of the question we need to look for an alternative, luckily the Threading namespace gives a good way of handling this scenario via the
AutoResetEvent/ManualResetEvent (the difference being that
AutoResetEvent will set it’s state to false after being signalled automatically) that’s great we now have a mechanism for telling the thread to wait however how do we know when to signal the event and allow the test runner thread to carry on? at the moment we have no way of knowing when the
Processor has processed the
I don’t think it’s unreasonable to provide someway of knowing when the
DoWork is complete, we can provide a
WorkItemComplete event that can be subscribed to:
I have introduced the event the next step is to amend our unit test to take advantage of this event and to synchronize the test runner thread with the threadpool thread doing the work:
It shouldn’t be too hard to see what’s going on here, I create a new
ManualResetEvent and set it’s state to unsignalled, I subscribe to the
WorkComplete event via a lambda which simply sets the
ManualResetEvent to signalled, after the call the DoWork is made we wait for the
ManualResetEvent to be signalled. So let’s give the unit test another go:
Great! that worked a treat one thing to bear in mind is when waiting for the event to be signalled is what timeout to use, in the case above I used 3000 milliseconds you typically want to use something around this figure and should be used as a failover in case the event/method that your expecting to be called isn’t and you don’t get to signal the event, this will prevent the test runner thread from getting stuck.
You should now be more comfortable when having to unit test code that performs work in a separate thread, if you don’t want to expose an event to know when a particular task is finished you could use subclass and override to only provide the event on the test subclass object.