Obstacles of unit testing & methods to side step them – Part 2: multi-threading

Introduction

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
  • Multi-threading
  • External Dependencies
  • Sharing Static items
Prerequisites
  • 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).
Multi-threading

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:

   1: [TestFixture]

   2: public class when_adding_two_ints_together

   3: {

   4:     [Test]

   5:     public void should_give_us_the_correct_result()

   6:     {
   8:         var processor = new Processor();

   7:         var sut = new AddTwoIntsWorkItem(1, 2);

   9:         processor.DoWork(sut);

  10:  

  11:         Assert.That(sut.Result, Iz.EqualTo(3));

  12:     }

  13: }

So for the example I created a simple Processor object and as simple command based interface and a concrete object:

   1: public class Processor

   2: {

   3:     public void DoWork(IWorkItem workItem)

   4:     {

   5:         ThreadPool.QueueUserWorkItem((state) =>

   6:             {

   7:                 workItem.Work();

   8:                 Thread.Sleep(2000);

   9:             });

  10:     }

  11: }

  12:  

  13: public interface IWorkItem

  14: {

  15:     void Work();

  16: }

  17:  

  18: public class AddTwoIntsWorkItem : IWorkItem

  19: {

  20:     protected int first;

  21:     protected int second;

  22:     protected int result;        

  23:  

  24:     public AddTwoIntsWorkItem(int first, int second)

  25:     {

  26:         this.first = first;

  27:         this.second = second;

  28:     }

  29:  

  30:     public void Work()

  31:     {

  32:         result = first + second;

  33:     }

  34:  

  35:     public int Result

  36:     {

  37:         get { return result; }

  38:     }

  39: }

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:

image

Hmm… were expecting 3 but getting 0 strange, well not really let’s try to breakdown what’s happening:

image

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

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:

   1: public class Processor

   2: {

   3:     public void DoWork(IWorkItem workItem)

   4:     {

   5:         ThreadPool.QueueUserWorkItem((state) =>

   6:             {

   7:                 workItem.Work();

   8:                 Thread.Sleep(2000);

   9:                 RaiseWorkComplete();

  10:             });

  11:     }

  12:  

  13:     protected void RaiseWorkComplete()

  14:     {

  15:         if (WorkComplete != null)

  16:             WorkComplete(this, EventArgs.Empty);

  17:     }

  18:  

  19:     public EventHandler WorkComplete;

  20: }

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:

   1: [Test]

   2: public void should_give_us_the_correct_result()

   3: {

   4:     // this is used to synchronize between this current thread &

   5:     // the thread that performs the work in Processor

   6:     var evt = new ManualResetEvent(false); 

   7:     var sut = new AddTwoIntsWorkItem(1, 2);

   8:     var processor = new Processor();

   9:     

  10:     processor.WorkComplete += (sender, e) => evt.Set(); // signal that work is done

  11:     processor.DoWork(sut);

  12:     // tells current thread to wait till work has been done in Processor

  13:     evt.WaitOne(3000); 

  14:  

  15:     Assert.That(sut.Result, Iz.EqualTo(3));

  16: }

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:

image 

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.

Advertisements

2 thoughts on “Obstacles of unit testing & methods to side step them – Part 2: multi-threading

  1. Pingback: Unit testing threading using rhino mocks « Journal of a software dev
  2. Салат из фасоли с яблоком и соленым огурцом. 2 стакана фасоли, 1 яблоко, 1 соленый огурец, 1 луковица, 1 яйцо, 1 стакан сметаны, красный перец, соль по вкусу, зелень петрушки. Фасоль замочите на несколько часов в холодной воде, затем отварите в подсоленной воде и охладите. Готовую фасоль смешайте с нарезанными кубиками огурцом, яйцом, луком и яблоком, полученную массу посолите, посыпьте перцем и заправьте сметаной. Готовый салат выложите в салатницу, сверху посыпьте мелко нарезанной зеленью петрушки.
    украинские блюда рецепты
    Напиток с саке и мятным ликером. 100 г белого вина, 30 г мятного ликера, 20 г саке, 1 яичный желток, 20 г сахара, 50 г апельсинового сока, колотый лед, половина апельсина. Яичный желток влить в шейкер, добавить сахар, белое вино, мятный ликер, саке, апельсиновый сок, положить колотый лед, все перемешать и взбить. Готовый напиток перелить в бокалы или пиалы и подать к столу, предварительно охладив и украсив дольками апельсина.

Comments are closed.