How to mock static methods

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.

Leave a Comment