Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / unit-testing

Unit Testing Classes That Use the System.Timer

5.00/5 (1 vote)
3 Jan 2016CPOL2 min read 23.3K  
How to replace the System.Timer with a mock object when testing

Introduction

This tip considers how best to replace System.Timer with a mock object to allow methods that use the timer to be tested effectively.

Why Create a Mock Timer?

It’s difficult to meet the basic requirements for a unit test if the method has an active timer as a dependency. It’s generally accepted that Unit tests should be executed entirely in memory, be self-contained and be exactly repeatable. But the Timer uses the system timer so the test is not self-contained, neither is it in a constant state as the timer is busy counting down some variable. If it’s not exactly repeatable, a different test is being run each time and, if it depends upon an external item it is, at least in part, an integration test of whatever it interfaces with.

What Exactly is Being Tested?

An important consideration is that the Timer itself is not being tested. What is tested is how the Timer interacts with the object under test (by way of firing the Elapsed event) and also how the object under test interacts with the timer through the timer’s various setup methods and action commands.

Constructing an ITimer Interface

Unit testing would be very easy to do if System.Timer implemented an interface (ITimer) that exposed the timer’s public methods, properties and events. All that would need to be done is to code using the ITimer interface and then employ a mock ITimer instance when testing. But Timer does not explicitly implement that sort of interface so it’s necessary to write your own. Something like:

C#
public interface ITimer
  {
      #region Public Events

      event ElapsedEventHandler Elapsed;

      #endregion

      #region Public Properties

      double Interval { get; set; }

      #endregion

      #region Public Methods and Operators

      void Dispose();

      void Start();

      void Stop();

      #endregion
  }

Running the Tests

Even with the ITimer interface in place, you can’t write:

C#
ITimer myTimer= new System.Timer();

As the system.Timer does not explicitly implement the ITimer interface, to get around this problem, it’s necessary to define a subclass of Timer that implements ITimer. Something like this:

C#
public class WatchDogTimer : Timer, ITimer { }

The derived timer (WatchDogTimer) explicitly implements the ITimer interface and has inherited all the functionality of the System.Timer. It’s now possible to do tests like this:

C#
//using the excellent NSubstitute mocking framework.
 [TestMethod]
        public void SetAlarm_SetsTimerInterval_To_INTERVAL_ThenCalls_TimerStart()
        {
            //Assemble
            const int INTERVAL=6;
            IBell bell = Substitute.For<IBell>();
            ITimer timer = Substitute.For<ITimer>();
            Alarm alarm = new Alarm(timer, bell);
            //Act
            alarm.SetAlarm(INTERVAL);
            //Assert
            Received.InOrder(() =>
            {
                timer.Interval = INTERVAL;
                timer.Start();               
            });
        }
  [TestMethod]
        public void TimerLapsedEvent_Results_In_A_CallTo_BellRing()
        {
            //Assemble
            IBell bell = Substitute.For<IBell>();
            ITimer timer = Substitute.For<ITimer>();
            Alarm alarm = new Alarm(timer, bell);
            //Act
            //Need to raise the Event with a null ElapsedEventArgs instance
            //as its constructor is internal
            timer.Elapsed += Raise.Event<ElapsedEventHandler>(this, null);
            //Assert
            bell.Received().Ring();          
        }

One Last Thing

There is a downside to this approach, the intelliSense that usually accompanies the System.Timer is lost, but with descriptive method names like Start and Stop, that’s not such a big deal.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)