65.9K
CodeProject is changing. Read more.
Home

Unit Testing Classes That Use the System.Timer

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1 vote)

Jan 3, 2016

CPOL

2 min read

viewsIcon

23353

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:

  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:

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:

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:

//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.