How to unit test a timer based on System.Threading.Timer in .NET The System.Threading.Timer has a callback method
4 Answers
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);
}

- 120,909
- 25
- 266
- 342
-
This looks good, but why do you create your own delegate rather than just using the TimerCallback delegate from the BCL? – Shaun Mar 21 '13 at 12:04
-
1@Shaun: Because that delegate is not a valid event handler. Event handlers have a specific signature: `object sender, TEventArgs e` where `TEventArgs : EventArgs`. – Aaronaught Mar 21 '13 at 23:32
-
Mind you, these days with closures being so readily supported, if I wanted something quick and dirty, I don't know if I'd bother with all of the event declaration and wire-up, I might just pass in an `Action` instead. – Aaronaught Mar 21 '13 at 23:34
-
Good point regarding the standard event handler signature, that's bitten me before when trying to use Moq to raise events that have non-standard signatures. – Shaun Mar 28 '13 at 16:20
I will test it in the same way as any other class but with short time intervals as to avoid the unit test to run a long time. Another approach is to test your own code only and using a mock timer (eg. NMock), but it depends how your code design is. Can you post some code snippets?

- 2,851
- 3
- 34
- 41
If the time simply changes the callback method then you just need to check the correctness of that method, not how the system time works.
Besides that, I recommend using the MultimediaTimer instead - it is much more accurate.

- 17,324
- 5
- 69
- 111
-
Say I want a timer that fires once an hour. Do you recommend the `MultimediaTimer` for everyone? :) – Tim Robinson Mar 02 '10 at 22:48
-
Yes, and combine it with .NET's Dispatcher if you need to return the UI's thread. – Danny Varod Mar 03 '10 at 06:51
Hopefully whatever this feature is a larger part of allows the timer interval to be configured. You should be able to use that interface to inject a short interval suitable for unit testing. I do have the question the value of this test: Are you testing the interval (pointless) or that the routine is actually called approximately every so often?

- 8,092
- 4
- 32
- 43