I’ve been guilty of using a very nasty pattern recently to write unit (and integration) tests for EasyNetQ. Because I’m dealing with inherently multi-threaded code, it’s difficult to know when a test will complete. Up to today, I’d been inserting Thread.Sleep(x) statements to give my tests time to complete before the test method itself ended.
Here’s a contrived example using a timer:
[Test]
public void SampleTestWithThreadSleep()
{
var timerFired = false;
new Timer(x =>
{
timerFired = true;
}, null, someAmountOfTime, Timeout.Infinite);
Thread.Sleep(2000);
timerFired.ShouldBeTrue();
}
I don’t know what ‘someAmountOfTime’ is, so I’m guessing a reasonable interval and then making the thread sleep before doing the asserts. In most cases the ‘someAmountOfTime’ is probably far less than the amount of time I’m allowing. It pays to be conservative in this case :)
My excellent colleague Michael Steele suggested that I use an AutoResetEvent instead. To my shame, I’d never spent the time to really understand the thread synchronisation methods in .NET, so it was back to school for a few hours while I read bits of Joseph Albahari’s excellent Threading in C#.
An AutoResetEvent allows you to block one thread while waiting for another thread to complete some task; ideal for this scenario.
Here’s my new test:
[Test]
public void SampleTestWithAutoResetEvent()
{
var autoResetEvent = new AutoResetEvent(false);
var timerFired = false;
new Timer(x =>
{
timerFired = true;
autoResetEvent.Set();
}, null, someAmountOfTime, Timeout.Infinite);
autoResetEvent.WaitOne(2000);
timerFired.ShouldBeTrue();
}
If you create an AutoResetEvent by passing false into its constructor it starts in a blocked state, so my test will run as far as the WaitOne line then block. When the timer fires and the Set method is called, the AutoResetEvent unblocks and the assert is run. This test only runs for ‘someAmountOfTime’ not for the two seconds that the previous one took; far better all round.
Have a look at my EasyNetQ commit where I changed many of my test methods to use this new pattern.