How to unit test a timer based on System.Threading.Timer in .NET The System.开发者_JS百科Threading.Timer has a callback method
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);
}
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?
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.
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?
精彩评论