Вы можете протестировать его, не создавая прямой зависимости от System.Threading.Timer
. Вместо этого создайте интерфейс ITimer
и обертку вокруг System.Threading.Timer
, которая его реализует.
Сначала необходимо преобразовать обратный вызов в событие, чтобы его можно было сделать частью интерфейса:
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; }
}
Затем создайте интерфейс:
public interface ITimer
{
void Change(TimeSpan dueTime, TimeSpan period);
event TimerEventHandler Tick;
}
И обертка:
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;
}
Очевидно, вы добавили бы любые перегрузки конструктора и / или Change
метода, которые вам нужно использовать из Threading.Timer
. Теперь вы можете протестировать все, что угодно, в зависимости от ITimer
с фальшивым таймером:
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;
}
Всякий раз, когда вы хотите смоделировать тик, просто наберите RaiseTickEvent
на подделке.
[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);
}