Multi-threaded unit testing
I once said that “Multithreading does a lot of things, but I don’t think ‘simplification’ is ever one of them“. Even with the simplest, most beginner level of no-frills threading, I find this to be true.
As an example, I was writing unit tests for my MonoDroid app that I recently decided to refactor with the MVP pattern (so I could increase test coverage). There’s only one place in the whole app where I use multiple threads. When the user clicks on a portfolio, there’s a web service call to get the latest stock prices (and volume, and other stock information). Because this can take a while, I want to show the user a nice animated “please wait” dialog on the UI while the web service (and other stuff) runs on a background thread. That all works great, and provides a preferable user experience to a blank screen doing nothing. (Note that I tried doing this without threads and I couldn’t get it to work–this could be a failure on my part).
However, when I got to the part where I’m testing the presenter’s orchestration of the above multi-threaded scenario, my tests started getting inconsistent results. I had also started using MSpec and JustMock at this time, and so I immediately assumed I was using those tools wrong. However, I should have immediately known it was a race condition, because sometimes my tests would all pass, and sometimes they wouldn’t. Seems obvious in retrospect.
What would happen is that sometimes my background thread would finish before the test assertions, and sometimes it wouldn’t. It was a huge relief once I figured that out, but now I have another problem: how do I test it?
My solution seems a little hacky, but it works. I was reminded of stories of people who wanted to test what would happen to certain parts of code at different times of the day. I.e. service X should do one thing in the morning, and another thing in the evening. But you can’t really inject a mock into every place the DateTime class is used. So, one approach is to simply wrap it in a service and put it behind an interface. And that’s exactly what I did with my threading code (which is a PostSharp aspect):
Then, when testing, before I do anything else, I set the ThreadingService to be some service that just runs the callback on the main thread, instead of putting it into a worker thread. Like so:
Since my unit tests don’t care about user experience (or even touch the UI implemention), I can now test that the presenter is doing what it’s supposed to. Is this a good general strategy for unit testing threaded code? Probably not. But maybe!