Unit test System.Threading.Timer in .NET

.net, asynchronous, multithreading, timer

Solution

You can unit-test it by not actually creating a direct dependency on `System.Threading.Timer`. Instead, create an `ITimer` interface, and a wrapper around `System.Threading.Timer` that implements it.

First you need to convert the callback to an event, so that it can be made part of an interface:

public delegate void TimerEventHandler(object sender, TimerEventArgs e);

public class TimerEventArgs : EventArgs
{
    public TimerEventArgs(object state)
    {
        this.State = state;
    }

    public object State { get; private set; }
}

Then create an interface:

public interface ITimer
{
    void Change(TimeSpan dueTime, TimeSpan period);
    event TimerEventHandler Tick;
}

And a wrapper:

public class ThreadingTimer : ITimer, IDisposable
{
    private Timer timer;

    public ThreadingTimer(object state, TimeSpan dueTime, TimeSpan period)
    {
        timer = new Timer(TimerCallback, state, dueTime, period);
    }

    public void Change(TimeSpan dueTime, TimeSpan period)
    {
        timer.Change(dueTime, period);
    }

    public void Dispose()
    {
        timer.Dispose();
    }

    private void TimerCallback(object state)
    {
        EventHandler tick = Tick;
        if (tick != null)
            tick(this, new TimerEventArgs(state));
    }

    public event TimerEventHandler Tick;
}

Obviously you would add whatever overloads of the constructor and/or `Change` method you need to use from the `Threading.Timer`. Now you can unit test anything depending on `ITimer` with a fake timer:

public class FakeTimer : ITimer
{
    private object state;

    public FakeTimer(object state)
    {
        this.state = state;
    }

    public void Change(TimeSpan dueTime, TimeSpan period)
    {
        // Do nothing
    }

    public void RaiseTickEvent()
    {
        EventHandler tick = Tick;
        if (tick != null)
            tick(this, new TimerEventArgs(state));
    }

    public event TimerEventHandler Tick;
}

Whenever you want to simulate a tick, just call `RaiseTickEvent` on the fake.

[TestMethod]
public void Component_should_respond_to_tick
{
    ITimer timer = new FakeTimer(someState);
    MyClass c = new MyClass(timer);
    timer.RaiseTickEvent();
    Assert.AreEqual(true, c.TickOccurred);
}

Problem

How to unit test a timer based on System.Threading.Timer in .NET The System.Threading.Timer has a callback method

Original source