Blogtronica

Thoughts, ramblings and insights

netronica.io

Multi-threaded unit test tip

Handy tip of the day.

Have you had code that runs in a thread and performs an action at some later point

e.g.

public class Worker  
{
    private IEventICareAbout _eventHandler;
    public Worker(IEventICareAbout eventHandler)
    {
        _eventHandler = eventHandler;
    }
}

public void DoWork()  
{
    Task.Run(() =>
             {
                 Thread.Sleep(TimeSpan.FromMilliseconds(100));
                 _eventHandler.Fire();
             });
}

And have you seen or written a test that sleeps to give the thread some time to run?

[Fact]
public void TestSlowProcess()  
{
    var handlerMock = new Mock<IEventICareAbout>();
    var sut = new Worker(handlerMock.Object);
    sut.DoWork();

    //Need to wait because DoWork won't immediately call Fire
    Thread.Sleep(TimeSpan.FromSeconds(5));

    handlerMock.Verify(h=>h.Fire(),Times.AtLeastOnce);
}

Manualreset event to the rescue!

Call set() on the callback, and wait for it to be called. In order to make the test fail gracefully you can supply a timespan for the maximum time we should wait.

[Fact]
public void TestSlowProcess()  
{
    var resetEvent = new ManualResetEvent(false);
    var handlerMock = new Mock<IEventICareAbout>();
    handlerMock.Setup(o => o.Fire()).Callback(() => resetEvent.Set());
    var sut = new Worker(handlerMock.Object);
    sut.DoWork();

    //Wait up to 5 seconds for the work to complete            
    Assert.True(resetEvent.WaitOne(TimeSpan.FromSeconds(5)),"waited 5 seconds, and Fire was not called");
    handlerMock.Verify(h=>h.Fire(),Times.AtLeastOnce);
}

Photo credit https://www.flickr.com/photos/sethoscope/37547604