I am struggling more than one day already with testing of my System.Threading.Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);
overload.
Basing on this solution I created my ThreadingTimer, FakeTimer, that implements ITimer:
public interface ITimer
{
bool Change(int dueTime, int period);
bool IsDisposed { get; }
void Dispose();
}
public class ThreadingTimer : ITimer, IDisposable
{
private Timer _timer;
public ThreadingTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
_timer = new Timer(callback, state, dueTime, period);
}
public bool IsDisposed { get; set; }
public void Dispose()
{
IsDisposed = true;
Dispose();
GC.SuppressFinalize(this);
}
public bool Change(int dueTime, int period)
{
_timer.Change(dueTime, period);
return true;
}
}
public class FakeTimer : ITimer
{
private object state;
public FakeTimer(TimerCallback callback, object state)
{
this.state = state;
}
public bool IsDisposed { get; set; }
public bool Change(int dueTime, int period)
{
return true;
}
public void Dispose()
{
IsDisposed = true;
Dispose();
GC.SuppressFinalize(this);
}
}
In my service class I want to make usage of my ThreadingTimer:
public ITimer Timer { get; set; }
private void StartUpdates()
{
Task.Run(() =>
{
Timer = new ThreadingTimer(StartUpdatingVessels, null, TimeSpan.Zero, TimeSpan.FromHours(24));
}, Token);
}
BUT when it comes to unit testing, I have no idea and understanding how to make usage and advantage of implementing of ITimer in my FakeTimer. Because if in my test will be called method StartUpdates()
, new instance of ThreadingTimer will be created, even if already Timer prop has assigned FakeTimer:
[Fact]
public void SetUpdatingStarted_CalledWhenUpdatingAlreadyStarted_DisposesTimer()
{
_timedUpdateControl.Timer = new FakeTimer(CallbackTestMethod, null);
bool intendedToStartUpdates = false;
_timedUpdateControl.StartOrStopUpdates(intendedToStartUpdates); //that methid calls private method StartUpdates() and creates instance for ITimer
//assert something
}
How hould I mock it there? (I am using Moq framework in test project).
One solution is to use Dependency Injection:
public interface ITimer
{
event Action Elapsed;
void StartTimer(int dueTime, int period);
bool IsDisposed { get; }
void Dispose();
}
public class TimedUpdateControl
{
private ITimer timer;
public TimedUpdateControl(ITimer timer)
{
this.timer = timer;
this.timer.Elapsed += () => { // do something }
}
private void StartUpdates()
{
Task.Run(() =>
{
timer.StartTimer(TimeSpan.Zero, TimeSpan.FromHours(24));
}, Token);
}
}
The test:
[Fact]
public void SetUpdatingStarted_CalledWhenUpdatingAlreadyStarted_DisposesTimer()
{
var timedUpdateControl = new TimedUpdateControl(new FakeTimer());
bool intendedToStartUpdates = false;
timedUpdateControl.StartOrStopUpdates(intendedToStartUpdates);
//assert something
}