The need to mock static methods in order to add a unit test is a very common problem. It’s often the case that these static methods are in third-party libraries. There are many utility libraries that are completely made up of static methods. While this makes them very easy to use, it makes them really difficult to test.
The way to mock a static method is by creating a class that wraps the call, extracting an interface, and passing in the interface. Then from your unit tests you can create a mock of the interface and pass it in.
In this article I’ll show an example of code that calls a static method and how to refactor it so the static method can be mocked.
Example of code calling a static method
MessageProcessor class
This class is using Newtonsoft.Json for JSON deserialization. It’s calling the static method JsonConvert.DeserializeObject().
using Newtonsoft.Json;
namespace MessageLibrary
{
public class MessageProcessor
{
public string GetMessageText(string messageJson)
{
if (string.IsNullOrWhiteSpace(messageJson))
{
throw new MessageParsingException("Invalid message. The message is null/empty/whitespace");
}
return JsonConvert.DeserializeObject<Message>(messageJson).Text;
}
}
}
Code language: C# (cs)
Note: Message and MessageParsingException are referenced here, but I’m intentionally not showing the code for these classes.
Unit test that is currently not mocking the static method
Since this test is not mocking the static method, the call to GetMessageText() is really deserializing the JSON.
[TestMethod()]
public void GetMessageText_WhenTextNull_ReturnsNull()
{
//arrange
var messageProcessor = new MessageProcessor();
//act
var actualText = messageProcessor.GetMessageText("{\"Id\":1, \"Text\":null}");
//assert
Assert.IsNull(actualText);
}
Code language: C# (cs)
Refactoring step 1 – Extract an interface and pass it in
The root problem is that calling the static method actually creates a hardcoded dependency. The first step is to pass in the dependency instead of hardcoding it. Newtonsoft.Json.JsonConvert is a static utility class that doesn’t implement an interface, so we’ll need to extract an interface and pass it in.
Extract an interface
I want to mock DeserializeObject(), so I created an interface that contains a method that has the same signature. The only difference is it’s not static.
namespace MessageLibrary
{
public interface IJsonUtility
{
T DeserializeObject<T>(string value);
}
}
Code language: C# (cs)
Pass in the interface
I changed MessageProcessor by passing in the IJsonUtility dependency, and I’m calling DeserializeObject() on this object instead of calling the static method.
namespace MessageLibrary
{
public class MessageProcessor
{
public string GetMessageText(string messageJson, IJsonUtility jsonUtility)
{
if (string.IsNullOrWhiteSpace(messageJson))
{
throw new MessageParsingException("Invalid message. The message is null/empty/whitespace");
}
return jsonUtility.DeserializeObject<Message>(messageJson).Text;
}
}
}
Code language: C# (cs)
Refactoring step 2 – Create the wrapper
I created a class that implements the interface. This class wraps the DeserializeObject() static method.
using Newtonsoft.Json;
namespace MessageLibrary
{
public class NewtonsoftJsonWrapper : IJsonUtility
{
public T DeserializeObject<T>(string value)
{
return JsonConvert.DeserializeObject<T>(value);
}
}
}
Code language: C# (cs)
Refactoring step 3 – Create the mock
Now that I’m passing in the IJsonUtility interface, I can create a mock and control the behavior of the DeserializeObject() method.
[TestMethod()]
public void GetMessageText_WhenTextNull_ReturnsNull()
{
//arrange
var messageProcessor = new MessageProcessor();
var mockJsonUtility = new Mock<IJsonUtility>();
mockJsonUtility.Setup(t => t.DeserializeObject<Message>(It.IsAny<string>()))
.Returns(new Message() { Text = null });
//act
var actualText = messageProcessor.GetMessageText("test", mockJsonUtility.Object);
//assert
Assert.IsNull(actualText);
}
Code language: C# (cs)
Notice that I no longer need to pass in valid JSON, because it’s not actually deserializing anymore. The ability to mock out behavior simplifies tests, and you can focus on what really matters – your code’s behavior.
In case you don’t want to refactor
You can use PowerMock (Java) or TypeMock Isolator (C#) to mock anything, including static methods. The need to use these libraries is often to cover up design smells and I wouldn’t recommend using them unless you really have to.