Mocking instances created within the tested class in .NET

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 dependency injectionDependency injection is the act of providing the required instances for an object. and mockingMocking is a process that allows you to create a mock object that can be used to simulate the behavior of a real object. classes is necessary.

Let's understand how mocking instances are created within the tested class using the following code example.

Problematic code

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 testableTestable code is a code that can be tested by verifying its behavior and output..

public class Oven
{
private int temperature;
private bool hasStarted;
public void Start()
{
// Sets the variable value that indicates time is started or not
hasStarted = true;
// Sets the temperature variable value
temperature = 200;
// Creates a timer that shall elapse in 20 minutes
var dingTimer = new Timer(TimeSpan.FromMinutes(20).TotalMilliseconds);
// Subscribes to the elapsed event
dingTimer.Elapsed += Timer_Elapsed;
// Starts the timer
dingTimer.Start();
}
private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
{
// Do something
}
}

Solution

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.

nunit
Timer.cs
Tests.cs
Oven.cs
TimerFactory.cs
ITimerFactory.cs
ITimer.cs
public interface ITimerFactory
{
ITimer Create();
}

Code explanation

  • 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