We all know how important it is to have your code covered with unit tests. But sometimes unit testing might become challenging when it comes to time and timers specifically. In order to effectively address the issue of controlling time as well as timers it will be necessary to mock both timers and time. Let’s imagine that we have an implementation of the scheduler. Scheduler is based on System.Threading.Timer and will schedule execution of another set of tasks (closest according to its schedules).

In case of timers it will be necessary to create a timer interface in order to mock it later on in tests. The code might look like following:

1
2
3
4
5
6
public interface INonIntervalTimer
{
  event EventHandler Elapsed;
  DateTime Now { get; }
  bool Change(TimeSpan dueTime);
}

Real implementation of NonIntervalThreadingTimer can look very simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
internal class NonIntervalThreadingTimer : INonIntervalTimer
{
  private readonly Timer _timer;
  private readonly Action _callback;

  public NonIntervalThreadingTimer(Action callback)
  {
    _callback = callback ?? (() => { });
    _timer = new Timer(_ => Callback());
  }

  public event EventHandler Elapsed;

  public DateTime Now { get { return DateTime.Now; } }

  public bool Change(TimeSpan dueTime)
  {
    return _timer.Change(dueTime, TimeSpan.FromMilliseconds(-1));
  }

  private void Callback()
  {
    _callback();
    OnElapsed();
  }

  private void OnElapsed()
  {
    var handler = Elapsed;
    if (handler != null)
    {
      handler(this, EventArgs.Empty);
    }
  }
}

Then for testing purposes (in testing library) it will be necessary to create an INonIntervalTimer implementation, e.g. like following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
internal class NonIntervalControlledTimer : INonIntervalTimer
{
  private DateTime _now = DateTime.MinValue;
  private DateTime _changedOn = DateTime.MinValue;
  private TimeSpan _dueTime = TimeSpan.FromMilliseconds(-1);
  private bool _hasProcessed;
  private readonly Action _callback;

  public NonIntervalControlledTimer(Action callback, DateTime now)
  {
    _callback = callback ?? (() => { });
    SetupNow(now);
  }

  public event EventHandler Elapsed;

  public DateTime Now { get { return _now; } }

  public bool Change(TimeSpan dueTime)
  {
    _changedOn = _now;
    _hasProcessed = false;
    _dueTime = dueTime;
    if (_dueTime == TimeSpan.FromMilliseconds(0))
    {
      ProcessIfNecessary();
    }
    return true;
  }

  public void SetupNow(DateTime now)
  {
    if (now < _now)
    {
      throw new ArgumentException(
        "Non interval controlled timer can only move forward in time");
    }
    _now = now;
    ProcessIfNecessary();
  }

  private void ProcessIfNecessary()
  {
    // check if timer is not set to start
    if (_dueTime == TimeSpan.FromMilliseconds(-1))
    {
      return;
    }
    // if it has not yet been processed and it's the right time
    if (!_hasProcessed && _now >= _changedOn + _dueTime)
    {
      _callback();
      _hasProcessed = true;
      OnElapsed();
    }
  }

  private void OnElapsed()
  {
    var handler = Elapsed;
    if (handler != null)
    {
      handler(this, EventArgs.Empty);
    }
  }
}

Now, let’s imagine that there is an implementation of scheduler available somewhere in the class named Scheduler and it is using INonIntervalTimer timer implementation to schedule its next task(s). In this case unit-testing will be possible with using ControlledNonIntervalTimer implementation the following way:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[TestClass]
public class SchedulerShould
{
  [TestMethod]
  public void ExecutePreScheduledTasks_OnStartup()
  {
    // Arrange var hitSchedulesCounter = 0;
    Action onScheduleHit = () => { hitSchedulesCounter++; };
    var executedTasksCounter = 0;
    Action onTaskExecuted = () => { executedTasksCounter++; };
    var now = new DateTime( 2000, 1, 1, 0, 0, 0 );
    var timer = new NonIntervalControlledTimer(onScheduleHit, now);
    var scheduler = new Scheduler(timer);
    var startTime = now - TimeSpan.FromHours(1);
    var endTime = DateTime.MaxValue;
    var schedule = Schedule.Create(
      ScheduleStrategy.EveryNMinutes,
      5,
      startTime,
      endTime);
    scheduler.AddTasks(
      schedule,
      new FakeTask(onTaskExecuted),
      new FakeTask(onTaskExecuted));

    // Act & Assert
    scheduler.Start();
    Assert.AreEqual(
      1, hitSchedulesCounter,
      "Scheduler didn't hit correct number of schedules");
    Assert.AreEqual(
      2, executedTasksCounter,
      "Scheduler didn't execute task according to schedule");
    now += TimeSpan.FromMinutes(5);
    timer.SetupNow(now);
    Assert.AreEqual(
      2, hitSchedulesCounter,
      "Scheduler didn't hit correct number of schedules");
    Assert.AreEqual(
      4, executedTasksCounter,
      "Scheduler didn't execute task according to schedule");
    scheduler.Stop();

    Assert.AreEqual(
      2, hitSchedulesCounter,
      "Scheduler didn't hit correct number of schedules");
    Assert.AreEqual(
      4, executedTasksCounter,
      "Scheduler didn't execute task according to schedule");
  }
}

Here you go! It should give you an opportunity to test the code dependent on INonIntervalTimer in a real unit-test manner. And remember how good is it to use interfaces when designing your code - it will lead to easier mocking and stubbing.