How to Have Full Control Over the Timer and Be Able to Reach 100% Coverage With Unit Tests When using in your application, you might face problems with abstracting it and being able to cover your modules with Unit Tests. System.Timers.Timer .NET C# In this article, we would be discussing the on how to conquer these challenges, and by the end, you would be able to achieve 100% coverage of your modules. Best Practices The Approach This is how we are going to approach our solution: Come up with a very simple example to work on. Start with the simple bad solution. Keep trying to enhance it till we reach the final format. Summarizing the lessons learned through our journey. The Example In our example, we will be building a simple which would do only one simple thing: to write to the console the . Console Application use a System.Timers.Timer date and time every second In the end, you should end up with this: As you can see, it is simple in terms of requirements, nothing fancy. Disclaimer Some best practices would be ignored/dropped in order to drive the main focus to the other best practices targeted in this article. In this article, we would focus on covering the module using with unit tests. However, the rest of the solution would not be covered with unit tests. If you would like to know more about this, you can check the article . System.Timers.Timer How to Fully Cover .NET C# Console Application With Unit Tests There are some third-party libraries that could be used to achieve nearly similar results. However, whenever possible, I would rather follow a native simple design than depend on a whole big third-party library. Bad Solution In this solution, we would directly use without providing a layer of abstraction. System.Timers.Timer The structure of the solution should look like this: It is a solution with only one project. UsingTimer Console TimerApp I intentionally invested some time and effort in abstracting into to prove that this would not solve our problem with the Timer. System.Console IConsole namespace TimerApp.Abstractions { public interface IConsole { void WriteLine(object? value); } } We would only need to use in our example; that’s why this is the only abstracted method. System.Console.WriteLine namespace TimerApp.Abstractions { public interface IPublisher { void StartPublishing(); void StopPublishing(); } } We have only two methods on the interface: and . IPublisher StartPublishing StopPublishing Now, for the implementations: using TimerApp.Abstractions; namespace TimerApp.Implementations { public class Console : IConsole { public void WriteLine(object? value) { System.Console.WriteLine(value); } } } is just a thin wrapper for . Console System.Console using System.Timers; using TimerApp.Abstractions; namespace TimerApp.Implementations { public class Publisher : IPublisher { private readonly Timer m_Timer; private readonly IConsole m_Console; public Publisher(IConsole console) { m_Timer = new Timer(); m_Timer.Enabled = true; m_Timer.Interval = 1000; m_Timer.Elapsed += Handler; m_Console = console; } public void StartPublishing() { m_Timer.Start(); } public void StopPublishing() { m_Timer.Stop(); } private void Handler(object sender, ElapsedEventArgs args) { m_Console.WriteLine(args.SignalTime); } } } is a simple implementation of . It is using a and just configuring it. Publisher IPublisher System.Timers.Timer It has the defined as a dependency. This is not a best practice from my point of view. If you want to understand what I mean, you can check the article . IConsole When Not To Use DI, IoC, and IoC Containers in .NET C# However, only for the sake of simplicity, we would just inject it as a dependency in the constructor. We are also setting the Timer interval to 1000 Milliseconds (1 Second), and setting the handler to write the Timer to the Console. SignalTime using TimerApp.Abstractions; using TimerApp.Implementations; namespace TimerApp { public class Program { static void Main(string[] args) { IPublisher publisher = new Publisher(new Implementations.Console()); publisher.StartPublishing(); System.Console.ReadLine(); publisher.StopPublishing(); } } } Here, in the class, we are not doing much. We are just creating an instance of the class and starting publishing. Program Publisher Running this should end up with something like this: Now, the question is, if you are going to write a unit test for the class, what can you do? Publisher Unfortunately, the answer would be: . not too much First, you are not injecting the Timer itself as a dependency. This means that you are hiding the dependency inside the class. Therefore, we can’t mock or stub the Timer. Publisher Second, let’s say that we modified the code so that the Timer is now injected in the constructor; still, the question would be, how to write a unit test and replace the Timer with a mock or stub? I hear someone shouting, “let’s wrap the Timer into an abstraction and inject it instead of the Timer.” Yes, that’s right, however, it is not that simple. There are some tricks that I am going to explain in the next section. Good Solution This is the time for a good solution. Let’s see what we can do about it. The structure of the solution should look like this: It is the same solution with a new project. UsingTimer Console BetterTimerApp , , and would be the same. IConsole IPublisher Console ITimer using System; namespace BetterTimerApp.Abstractions { public delegate void TimerIntervalElapsedEventHandler(object sender, DateTime dateTime); public interface ITimer : IDisposable { event TimerIntervalElapsedEventHandler TimerIntervalElapsed; bool Enabled { get; set; } double Interval { get; set; } void Start(); void Stop(); } } What we can notice here: We defined the new delegate . This delegate represents the event to be raised by our . TimerIntervalElapsedEventHandler ITimer You might argue that we don’t need this new delegate as we already have the native which is already used by . ElapsedEventHandler System.Timers.Timer Yes, this is true. However, you would notice that the event is providing as the event arguments. This has a private constructor, and you would not be able to create your own instance. Additionally, the property defined in the class is read-only. Therefore, you would not be able to override it in a child class. ElapsedEventHandler ElapsedEventArgs ElapsedEventArgs SignalTime ElapsedEventArgs There is a change request ticket opened for Microsoft to update this class, but up till the moment of writing this article, no changes were applied. Also, note that extends the . ITimer IDisposable Publisher using System; using BetterTimerApp.Abstractions; namespace BetterTimerApp.Implementations { public class Publisher : IPublisher { private readonly ITimer m_Timer; private readonly IConsole m_Console; public Publisher(ITimer timer, IConsole console) { m_Timer = timer; m_Timer.Enabled = true; m_Timer.Interval = 1000; m_Timer.TimerIntervalElapsed += Handler; m_Console = console; } public void StartPublishing() { m_Timer.Start(); } public void StopPublishing() { m_Timer.Stop(); } private void Handler(object sender, DateTime dateTime) { m_Console.WriteLine(dateTime); } } } It is almost the same as the old except for small changes. Now, we have the defined as a dependency that is injected through the constructor. The rest of the code would be the same. Publisher ITimer Timer using System; using System.Collections.Generic; using System.Linq; using System.Timers; using BetterTimerApp.Abstractions; namespace BetterTimerApp.Implementations { public class Timer : ITimer { private Dictionary<TimerIntervalElapsedEventHandler, List<ElapsedEventHandler>> m_Handlers = new(); private bool m_IsDisposed; private System.Timers.Timer m_Timer; public Timer() { m_Timer = new System.Timers.Timer(); } public event TimerIntervalElapsedEventHandler TimerIntervalElapsed { add { var internalHandler = (ElapsedEventHandler)((sender, args) => { value.Invoke(sender, args.SignalTime); }); if (!m_Handlers.ContainsKey(value)) { m_Handlers.Add(value, new List<ElapsedEventHandler>()); } m_Handlers[value].Add(internalHandler); m_Timer.Elapsed += internalHandler; } remove { m_Timer.Elapsed -= m_Handlers[value].Last(); m_Handlers[value].RemoveAt(m_Handlers[value].Count - 1); if (!m_Handlers[value].Any()) { m_Handlers.Remove(value); } } } public bool Enabled { get => m_Timer.Enabled; set => m_Timer.Enabled = value; } public double Interval { get => m_Timer.Interval; set => m_Timer.Interval = value; } public void Start() { m_Timer.Start(); } public void Stop() { m_Timer.Stop(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing && m_Handlers.Any()) { foreach (var internalHandlers in m_Handlers.Values) { if (internalHandlers?.Any() ?? false) { internalHandlers.ForEach(handler => m_Timer.Elapsed -= handler); } } m_Timer.Dispose(); m_Timer = null; m_Handlers.Clear(); m_Handlers = null; } m_IsDisposed = true; } ~Timer() { Dispose(false); } } } This is where almost all the magic happens. What we can notice here: Internally, we are using . System.Timers.Timer We applied the design pattern. That’s why you can see the , , , and . IDisposable private bool m_IsDisposed public void Dispose() protected virtual void Dispose(bool disposing) ~Timer() In the constructor, we are initializing a new instance of . We would refer to this as the in the rest of the steps. System.Timers.Timer Internal Timer For , , , and , we are just delegating the implementation to the Internal Timer. public bool Enabled public double Interval public void Start() public void Stop() For , this is the most important part; so let’s analyze it step by step. public event TimerIntervalElapsedEventHandler TimerIntervalElapsed What we need to do with this event is to handle when someone subscribes/unsubscribes to it from outside. In this case, we want to mirror this to the Internal Timer. In other words, if someone from outside is having an instance of our , he should be able to do something like this . ITimer t.TimerIntervalElapsed += (sender, dateTime) => { //do something } At this moment, what we should do is internally do something like . m_Timer.Elapsed += (sender, elapsedEventArgs) => { //do something } However, we need to keep in mind that the two handlers are not the same as they are actually of different types; and . TimerIntervalElapsedEventHandler ElapsedEventHandler Therefore, what we need to do is to wrap the coming in into a new internal . This is something we can do. TimerIntervalElapsedEventHandler ElapsedEventHandler However, we also need to keep in mind that, at some point, someone might need to unsubscribe a handler from the event. TimerIntervalElapsedEventHandler This means that, at this moment, we need to be able to know which handler corresponds to that handler so that we can unsubscribe it from the Internal Timer. ElapsedEventHandler TimerIntervalElapsedEventHandler The only way to achieve this is by keeping track of each handler and the newly created handler in a dictionary. This way, by knowing the passed in handler, we can know the corresponding handler. TimerIntervalElapsedEventHandler ElapsedEventHandler TimerIntervalElapsedEventHandler ElapsedEventHandler However, we also need to keep in mind that from outside, someone might subscribe to the same handler more than once. TimerIntervalElapsedEventHandler Yes, this is not logical, but still, it is doable. Therefore, for the sake of completeness, for each handler, we would keep a list of handlers. TimerIntervalElapsedEventHandler ElapsedEventHandler In most cases, this list would have only one entry unless in case of a duplicate subscription. And that’s why you can see this . private Dictionary<TimerIntervalElapsedEventHandler, List<ElapsedEventHandler>> m_Handlers = new(); public event TimerIntervalElapsedEventHandler TimerIntervalElapsed { add { var internalHandler = (ElapsedEventHandler)((sender, args) => { value.Invoke(sender, args.SignalTime); }); if (!m_Handlers.ContainsKey(value)) { m_Handlers.Add(value, new List<ElapsedEventHandler>()); } m_Handlers[value].Add(internalHandler); m_Timer.Elapsed += internalHandler; } remove { m_Timer.Elapsed -= m_Handlers[value].Last(); m_Handlers[value].RemoveAt(m_Handlers[value].Count - 1); if (!m_Handlers[value].Any()) { m_Handlers.Remove(value); } } } In the , we are creating a new , adding a record in the dictionary mapping this to , and finally subscribing to the Internal Timer. add ElapsedEventHandler m_Handlers TimerIntervalElapsedEventHandler In the , we are getting the corresponding list of handlers, selecting the last handler, unsubscribing it from the Internal Timer, removing it from the list, and removing the whole entry if the list is empty. remove ElapsedEventHandler Also worth mentioning, the implementation. Dispose protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing && m_Handlers.Any()) { foreach (var internalHandlers in m_Handlers.Values) { if (internalHandlers?.Any() ?? false) { internalHandlers.ForEach(handler => m_Timer.Elapsed -= handler); } } m_Timer.Dispose(); m_Timer = null; m_Handlers.Clear(); m_Handlers = null; } m_IsDisposed = true; } We are unsubscribing all the remaining handlers from the Internal Timer, disposing of the Internal Timer, and clearing the dictionary. m_Handlers Program using BetterTimerApp.Abstractions; using BetterTimerApp.Implementations; namespace BetterTimerApp { public class Program { static void Main(string[] args) { var timer = new Timer(); IPublisher publisher = new Publisher(timer, new Implementations.Console()); publisher.StartPublishing(); System.Console.ReadLine(); publisher.StopPublishing(); timer.Dispose(); } } } Here, we are still not doing much. It is almost the same as the old solution. Running this should end up with something like this: Time For Testing, The Moment of Truth Now, we have our final design. However, we need to see if this design really can help us cover our module with unit tests. Publisher The structure of the solution should look like this: I am using and for testing. You can for sure work with your preferred libraries. NUnit Moq TimerStub using System; using System.Collections.Generic; using BetterTimerApp.Abstractions; namespace BetterTimerApp.Tests.Stubs { public enum Action { Start = 1, Stop = 2, Triggered = 3, Enabled = 4, Disabled = 5, IntervalSet = 6 } public class ActionLog { public Action Action { get; } public string Message { get; } public ActionLog(Action action, string message) { Action = action; Message = message; } } public class TimerStub : ITimer { private bool m_Enabled; private double m_Interval; public event TimerIntervalElapsedEventHandler TimerIntervalElapsed; public Dictionary<int, ActionLog> Log = new(); public bool Enabled { get => m_Enabled; set { m_Enabled = value; Log.Add(Log.Count + 1, new ActionLog(value ? Action.Enabled : Action.Disabled, value ? "Enabled" : "Disabled")); } } public double Interval { get => m_Interval; set { m_Interval = value; Log.Add(Log.Count + 1, new ActionLog(Action.IntervalSet, m_Interval.ToString("G17"))); } } public void Start() { Log.Add(Log.Count + 1, new ActionLog(Action.Start, "Started")); } public void Stop() { Log.Add(Log.Count + 1, new ActionLog(Action.Stop, "Stopped")); } public void TriggerTimerIntervalElapsed(DateTime dateTime) { OnTimerIntervalElapsed(dateTime); Log.Add(Log.Count + 1, new ActionLog(Action.Triggered, "Triggered")); } protected void OnTimerIntervalElapsed(DateTime dateTime) { TimerIntervalElapsed?.Invoke(this, dateTime); } public void Dispose() { Log.Clear(); Log = null; } } } What we can notice here: We defined the enum to be used while logging the actions performed through our Timer stub. This would be used later to assert the internal actions performed. Action Also, we defined the class to be used for logging. ActionLog We defined the class as a stub of . We would use this stub later when testing the module. TimerStub ITimer Publisher The implementation is simple. Worth to mention that we added an extra method so that we can trigger the stub manually within a unit test. public void TriggerTimerIntervalElapsed(DateTime dateTime) We can also pass in the expected value of the so that we have a known value to assert against. dateTime PublisherTests using System; using BetterTimerApp.Abstractions; using BetterTimerApp.Implementations; using BetterTimerApp.Tests.Stubs; using Moq; using NUnit.Framework; using Action = BetterTimerApp.Tests.Stubs.Action; namespace BetterTimerApp.Tests.Tests { [TestFixture] public class PublisherTests { private TimerStub m_TimerStub; private Mock<IConsole> m_ConsoleMock; private Publisher m_Sut; [SetUp] public void SetUp() { m_TimerStub = new TimerStub(); m_ConsoleMock = new Mock<IConsole>(); m_Sut = new Publisher(m_TimerStub, m_ConsoleMock.Object); } [TearDown] public void TearDown() { m_Sut = null; m_ConsoleMock = null; m_TimerStub = null; } [Test] public void ConstructorTest() { Assert.AreEqual(Action.Enabled, m_TimerStub.Log[1].Action); Assert.AreEqual(Action.Enabled.ToString(), m_TimerStub.Log[1].Message); Assert.AreEqual(Action.IntervalSet, m_TimerStub.Log[2].Action); Assert.AreEqual(1000.ToString("G17"), m_TimerStub.Log[2].Message); } [Test] public void StartPublishingTest() { // Arrange var expectedDateTime = DateTime.Now; m_ConsoleMock .Setup ( m => m.WriteLine ( It.Is<DateTime>(p => p == expectedDateTime) ) ) .Verifiable(); // Act m_Sut.StartPublishing(); m_TimerStub.TriggerTimerIntervalElapsed(expectedDateTime); // Assert ConstructorTest(); m_ConsoleMock .Verify ( m => m.WriteLine(expectedDateTime) ); Assert.AreEqual(Action.Start, m_TimerStub.Log[3].Action); Assert.AreEqual("Started", m_TimerStub.Log[3].Message); Assert.AreEqual(Action.Triggered, m_TimerStub.Log[4].Action); Assert.AreEqual(Action.Triggered.ToString(), m_TimerStub.Log[4].Message); } [Test] public void StopPublishingTest() { // Act m_Sut.StopPublishing(); // Assert ConstructorTest(); Assert.AreEqual(Action.Stop, m_TimerStub.Log[3].Action); Assert.AreEqual("Stopped", m_TimerStub.Log[3].Message); } [Test] public void FullProcessTest() { // Arrange var expectedDateTime1 = DateTime.Now; var expectedDateTime2 = expectedDateTime1 + TimeSpan.FromSeconds(1); var expectedDateTime3 = expectedDateTime2 + TimeSpan.FromSeconds(1); var sequence = new MockSequence(); m_ConsoleMock .InSequence(sequence) .Setup ( m => m.WriteLine ( It.Is<DateTime>(p => p == expectedDateTime1) ) ) .Verifiable(); m_ConsoleMock .InSequence(sequence) .Setup ( m => m.WriteLine ( It.Is<DateTime>(p => p == expectedDateTime2) ) ) .Verifiable(); m_ConsoleMock .InSequence(sequence) .Setup ( m => m.WriteLine ( It.Is<DateTime>(p => p == expectedDateTime3) ) ) .Verifiable(); // Act m_Sut.StartPublishing(); m_TimerStub.TriggerTimerIntervalElapsed(expectedDateTime1); // Assert ConstructorTest(); m_ConsoleMock .Verify ( m => m.WriteLine(expectedDateTime1) ); Assert.AreEqual(Action.Start, m_TimerStub.Log[3].Action); Assert.AreEqual("Started", m_TimerStub.Log[3].Message); Assert.AreEqual(Action.Triggered, m_TimerStub.Log[4].Action); Assert.AreEqual(Action.Triggered.ToString(), m_TimerStub.Log[4].Message); // Act m_TimerStub.TriggerTimerIntervalElapsed(expectedDateTime2); // Assert m_ConsoleMock .Verify ( m => m.WriteLine(expectedDateTime2) ); Assert.AreEqual(Action.Triggered, m_TimerStub.Log[5].Action); Assert.AreEqual(Action.Triggered.ToString(), m_TimerStub.Log[5].Message); // Act m_Sut.StopPublishing(); // Assert Assert.AreEqual(Action.Stop, m_TimerStub.Log[6].Action); Assert.AreEqual("Stopped", m_TimerStub.Log[6].Message); } } } Now, as you can see, we have full control, and we can easily cover our module with unit tests. Publisher If we calculate the coverage, we should get this: As you can see, the module is 100% covered. For the rest, this is out of scope of this article, but you can simply cover it if you follow the approach in the article . Publisher How to Fully Cover .NET C# Console Application With Unit Tests Final Words You can do it. It is just a matter of splitting large modules into smaller ones, defining your abstractions, getting creative with tricky parts, and then you are done. If you want to train yourself more, you can check my other articles about some Best Practices. That’s it, hope you found reading this article as interesting as I found writing it. Also published here