Mocking in C# Unit testing

In our previous post, Unit Testing in C#, Calculator class under test does not have any dependencies. We need not create any other class instance other than Calculator’s. But this is not usually the case in production environment. Here we will see unit testing approach for the classes having one or more dependencies.

Before we start to explain mocking, remember that one of the concepts of Unit testing is isolation of class under test; this means that dependencies of the class under test should have minimum or no affect on test outcome. This becomes possible when we can mimic the dependencies in the test class with objects which we can control and dictate.

‘Mock’ is something which is not real, is an imitation of a real object. So, Mocking in Unit tests is creating replica objects of real dependencies of class under test, which we can control in test environment.

Let’s start by how we create these mock objects. We can implement mock objects by ourselves or better we should take help of mocking frameworks available such as NMock, Moq for C#, Gmock for C++, Mockito, JMock, EasyMock for Java.

Here for the demonstration, I have a Logger class which has two dependencies, IWriter and IDateTimeProvider, responsibilities for each is evident from the names of interfaces. Logger methods for e.g., LogInfo calls Write method of IWriter‘s implementation. Thus we need to verify in our unit tests whether call to the Write method is done with desired argument or not.

namespace LoggerLib
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Logger class.
/// </summary>
internal class Logger : ILogger
{
private IWriter writer;
private IDateTimeProvider dateTimeProvider;
private readonly string info = "Info";
private readonly string warning = "Warn";
private readonly string error = "Error";
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class.
/// </summary>
/// <param name="writer"></param>
public Logger(IWriter writer, IDateTimeProvider dateTimeProvider)
{
if (writer == null)
{
throw new ArgumentNullException("writer cannot be null!");
}
if (dateTimeProvider == null)
{
throw new ArgumentNullException("dateTimeProvider cannot be null!");
}
this.writer = writer;
this.dateTimeProvider = dateTimeProvider;
}
public void LogInfo(string infoText, [CallerMemberName] string memberName = "", [CallerFilePath] string callerFilePath = "")
{
this.writer.Write(string.Format("{0} {1}:{2} {3} {4}", LogTime, info, infoText, callerFilePath, memberName));
}
public void LogWarning(string warningText, [CallerMemberName] string memberName = "", [CallerFilePath] string callerFilePath = "")
{
this.writer.Write(string.Format("{0} {1}:{2} {3} {4}", LogTime, warning, warningText, callerFilePath, memberName));
}
public void LogError(string errorText, [CallerMemberName] string memberName = "", [CallerFilePath] string callerFilePath = "")
{
this.writer.Write(string.Format("{0} {1}:{2} {3} {4}", LogTime, error ,errorText, callerFilePath, memberName));
}
private string LogTime => this.dateTimeProvider.CurrentDateTime.ToString();
}
}
view raw Logger.cs hosted with ❤ by GitHub

Now, in unit test class for creating Logger instance, we need not to create instance of its dependencies, instead we can create mock objects.

namespace LoggerLibTests
{
using LoggerLib;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Diagnostics;
[TestClass]
public class LoggerTest
{
private Mock<IWriter> mockWriter;
private Mock<IDateTimeProvider> mockDateTimeProvider;
private Logger logger;
[TestInitialize]
public void TestInit()
{
mockWriter = new Mock<IWriter>();
mockDateTimeProvider = new Mock<IDateTimeProvider>();
logger = new Logger(mockWriter.Object, mockDateTimeProvider.Object);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void TestConstructor_WriterNull_ArgumentNullException()
{
// Arrange
logger = new Logger(null, mockDateTimeProvider.Object);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void TestConstructor_DateTimeProviderNull_ArgumentNullException()
{
// Arrange
logger = new Logger(mockWriter.Object, null);
}
[TestMethod]
public void ConstructorTest()
{
// Assert
Assert.IsNotNull(logger);
}
[TestMethod]
public void Test_LogInfo()
{
// Arrange
string textToLog = "SampleTestLog";
string memberName = new StackTrace().GetFrame(0).GetMethod().Name;
string callerFilePath = new StackFrame(0, true).GetFileName();
DateTime dateTimeNow = DateTime.Now;
this.mockDateTimeProvider.Setup(m => m.CurrentDateTime).Returns(dateTimeNow);
string expectedLogText = string.Format("{0} {1}:{2} {3} {4}", dateTimeNow.ToString(), "Info", textToLog, callerFilePath, memberName);
// Act
logger.LogInfo(textToLog);
// Assert
mockWriter.Verify(m => m.Write(expectedLogText), Times.Once);
}
}
}
view raw LoggerTest.cs hosted with ❤ by GitHub

In above LoggerTest class, we have used Moq framework, which gives powerful APIs such as Setup and Verify; using Setup, we can arrange desired return value from the mocked object’s properties and methods; using Verify, we can assert whether the call to the mocked object’s method is created or not.

For creating mock objects, Moq gives Mock<T> type where T is the type for which mock object is to be created. Usually for creating mock objects, dependencies of the class are preferred to be of interface types which both results in better abstraction and loose coupling.

Moq library can be download as nuget package from nuget website. Extract the package with WinZip or 7Zip and refer Moq.dll as assembly reference in your unit test project.

Find complete project solution here.

Design a site like this with WordPress.com
Get started