Mocks drastically reduce the amount of setup code needed for creating unit tests. However, sometimes, instances are created and disposed of within a method, leaving no opportunity to analyze what was created. For this answer, a thorough understanding of the
Let's understand how mocking instances are created within the tested class using the following code example.
In the Start
method below, we create a new timer which we don't have access to. This makes it impossible for us to verify what happened to it. We can say that this code is not
public class Oven{private int temperature;private bool hasStarted;public void Start(){// Sets the variable value that indicates time is started or nothasStarted = true;// Sets the temperature variable valuetemperature = 200;// Creates a timer that shall elapse in 20 minutesvar dingTimer = new Timer(TimeSpan.FromMinutes(20).TotalMilliseconds);// Subscribes to the elapsed eventdingTimer.Elapsed += Timer_Elapsed;// Starts the timerdingTimer.Start();}private void Timer_Elapsed(object? sender, ElapsedEventArgs e){// Do something}}
There are three necessary steps to make this code testable:
Create an ITimerFactory
and ITimer
interfaces, to mock and verify what happens with TimerFactory
and Timer
instances, respectively.
Create a TimerFactory
and Timer
to provide ITimerFactory
and ITimer
implementations, respectively.
Use the TimerFactory
in the Oven
class to create timers.
In the solution below, we can see the steps implemented. It is now possible to create unit tests for the oven class that can benefit from mocking frameworks.
public interface ITimerFactory{ITimer Create();}
In ITimer.cs
file: This file only contains the definition of the ITimer
interface. It does not have any executable code.
In Timer.cs
file: This file contains the implementation of the DefaultTimer
class, which implements the ITimer
interface. The DefaultTimer
class initializes an internal timer instance in its constructor, setting the interval based on the minutes between events. The Elapsed
event of the internal timer is mapped to the corresponding event of the DefaultTimer
class. This allows event handlers to be attached and detached from the Elapsed event of the DefaultTimer
class. The Start()
method starts the internal timer.
In ITimerFactory.cs
file: This file defines the ITimerFactory
interface, which declares a Create()
method for creating ITimer
instances.
In Oven.cs
file: This file contains the Oven
class, which represents an oven. The Oven
class has a constructor that takes an ITimerFactory
instance as a dependency. The Start()
method is called to start the oven. The Create()
method of the ITimerFactory
is called to create a timer object. An event handler (Timer_Elapsed
) is subscribed to the Elapsed
event of the timer. The timer is started by calling its Start()
method. A message is written to the console to indicate that the oven has been started.
In Tests.cs
file: This file contains the TestClass
class with the Start_ShouldStartTimer()
test method. The test method is annotated with the [Test]
attribute, indicating that it’s a unit test. Inside the test method: a mock object of the ITimerFactory
interface (timerFactoryMock
) is created using the Mock
class from the Moq
framework. Another mock object of the ITimer
interface (timerMock
) is created. The Create()
method of the ITimerFactory
mock is set up to return the ITimer
mock. An instance of the oven class is created, passing the ITimerFactory
mock as a parameter. The Start()
method of the Oven
instance is called. The Start()
method of the ITimer
mock is verified to ensure it has been called.
Free Resources