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 the Newtonsoft.Json utility for JSON deserialization. It’s calling the static method JsonConvert.DerserializeObject().

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; } } }

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); }

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); } }

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; } } }

Refactoring step 2 – Create the wrapper

I created a class that implements the IJsonUtility 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); } } }

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); }

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