Having become intrigued by Test Driven Development, I’ve spent a good deal of time getting to grips with the tools and techniques, but find I’m still coming accross scenarios that leave me wondering, “How on earth do I test this?”.
One of the biggest areas that’s given me grief is multi-threading. I spent some of today trying to decide the best way to test a worker class that does its work in a secondary thread and returns its completion status asynchronously.
The application structure generally looks like this:
MainForm <-> App <-> Worker Class
The GUI may invoke the Worker Class to do some work that takes a significant amount of time, so this is obviously threaded.
This forms a pretty standard MVC structure, so my initial plan was to use the MVC structure to feed back completion status through the App as would happen in a standard MVC model, treating the app as the controller, without the use of any events. I didn’t want to make use of an event here as I felt it would be introducing additional complexity when deciding where the event should be hooked and so on.
Whilst this felt like the simplest approach, it fell flat on not being very testable.
Making it Testable
In order to test the worker service the App is replaced with a mock object, which is fine for checking that the completion method is called, but fails in that it could not be monitored to see when the worker thread has finished.
A stop gap solution involved entering a while loop for 10 seconds until I was sure the thread was complete, before letting the test continue and validate that all the mocks we succesfully met.
Obviously this reeks, so I started looking at ways of exposing the worker thread so that I could monitor it directly, before realising that this was also leading me down the wrong path, as ideally the test shouldn’t care about the internals of what is being tested.
Had I gone down that road, it would most likely have led to problems when trying to test what happens when it is fired multiple times for example.
Having decided that, I relented and did what I was initially trying to avoid, and made use of an event for the completion of the work, as is documented in a number of places as the correct way to test worker threads.
It turns out this made the most sense as the App is responsible for creating the Worker Class in the first place, so is the perfect place to register for the completion event, so that it can then be passed to the UI in the same way that it was before, so the GUI encapsulation is still well maintained (Assuming an interface is used).
This can then be tested by using an anonymous delegate to signal when the waiting should be end, and I no longer have to do any nasty waiting for an arbitrary length of time!