Static methods can often seem like an excellent way to achieve reusability.
I mean, what’s not to like about static methods?
Write them once and call them whenever you want. The problem, however, arises when you need to test the C# code that calls a static method. It’s not straightforward to mock the static method for testing purposes.
There are three ways to test the code that calls static methods:
- Create a wrapper class and use dependency injection
- Use a static Func property
- Use the Extract and override call
If you have struggled with writing unit tests for code that uses static methods, this article is for you.
Why Unit Test Static Methods in C#
A static method is a special type of method that does not access any instance variables. You can call static methods directly from another class, and they are helpful in creating standalone functions that don’t require an instance of the class to operate.
Now, since it’s easy to call them from the whenever place you want, it’s easy to overuse them.
Let’s look at the following very simple code example:
public class VisitorService
{
public string CapturedImage { get; set; }
public string CapturedImageName { get; set; }
public void ClearCapturedImage()
{
if (CurrentSession.GetCurrentSession() == null)
{
CapturedImage = "";
CapturedImageName = "";
}
}
//other methods and properties are omitted...
}
The ClearCapturedImage method depends on the CurrentSession
.GetCurrentSession
static method. GetCurrentSession
talks to the HttpContext
class of the current request and returns the AppSession
object.
public static AppSession GetCurrentSession()
{
AppSession vmSession;
if (HttpContext.Current.Session["Session"] != null)
{
vmSession = HttpContext.Current.Session["Session"] as AppSession;
}
else
{
vmSession = HttpContext.Current.Session["Session"] as AppSession;
vmSession.UserId = null;
}
return vmSession;
}
As you can see, it’s not easy to test the code in its current state. You would have a hard time setting up the HttpContext.Current
class.
Even if you manage to initialize it, the code to see up everything in the code would be very complicated. And hard to maintain in the long run.
The other common usage of the static methods is to call the database or some other external dependency and return a result to the current code. To test that code in the original state, you would have to set up an integration test and connect to the test database.
However, since static methods are often part of the business logic, testing the code that uses them makes sense. That way, you can ensure that your code delivers the expected results.
You can either use one of the three approaches outlined below or experiment with combining those strategies.
Create a wrapper class and use dependency injection
The first approach uses a wrapper class to encapsulate the static method. To do so, first, create an interface that encapsulates the static method.
public interface ICurrentSessionWrapper
{
AppSession GetCurrentSession();
}
Next, create a class that implements the interface and wraps the call to the static method:
class CurrentSessionWrapper : ICurrentSessionWrapper
{
public AppSession GetCurrentSession()
{
return CurrentSession.GetCurrentSession();
}
}
Now, inject the dependency using the constructor.
public class VisitorService
{
private ICurrentSessionWrapper _sessionWrapper;
public VisitorService(ICurrentSessionWrapper sessionWrapper)
{
_sessionWrapper = sessionWrapper;
}
....
Instead of static method, you can now call the wrapper class:
public void ClearCapturedImage()
{
if (_sessionWrapper.GetCurrentSession() == null)
{
CapturedImage = "";
CapturedImageName = "";
}
}
Finally, you can write your test.
[Fact]
public void ClearCapturedImage_clears_captured_image_when_session_is_null()
{
AppSession session = null;
var fakeVisitorWrapper = new Mock<ICurrentSessionWrapper>();
fakeVisitorWrapper
.Setup(x => x.GetCurrentSession())
.Returns(session);
var visitorService = new VisitorService(fakeVisitorWrapper.Object)
{
CapturedImage = "image.png",
CapturedImageName = "initial name"
};
visitorService.ClearCapturedImage();
Assert.Equal(string.Empty, visitorService.CapturedImage);
Assert.Equal(string.Empty, visitorService.CapturedImageName);
}
The first step in the test is to set up the fake ICurrentSessionWrapper
object. Then, I use Moq mocking framework to create a new Mock
object. After that, use the Setup method to return null when GetCurrentSession gets called.
Now it’s easy to call the ClearCapturedImage
method and check the state of the CapturedImage
and CapturedImageName
after the call.
I have used Moq to mock the dependency I need in the test. Moq can help you to write tests much faster. I have a separate comprehensive article about Moq, so check it out.
Use a static Func property
The previous approach assumes you can easily inject a new dependency through the constructor.
However, in many legacy apps, it is hard to make any change to the constructor. The reason for that is that the class might be called from dozen of other places. A single change to the constructor requires changing all other places where that class is being initialized.
The workaround in C# is to create a new static Func that will wrap the call to the CurrentSession.GetCurrentSession
:
public class VisitorService
{
//1
public Func<AppSession> GetCurrentSession = () => CurrentSession.GetCurrentSession();
public string CapturedImage { get; set; }
public string CapturedImageName { get; set; }
public void ClearCapturedImage()
{
//2
if (GetCurrentSession() == null)
{
CapturedImage = "";
CapturedImageName = "";
}
}
}
There are two changes you need to make:
- Create a new Func static member and implement it to return the CurrentSession.GetCurrentSession().
- Call the new Func in the code.
Now you can write the unit test method:
[Fact]
public void ClearCapturedImage_clears_captured_image_when_session_is_null()
{
var visitorService = new VisitorService()
{
CapturedImage = "image.png",
CapturedImageName = "initial name"
};
visitorService.GetCurrentSession = () => null;
visitorService.ClearCapturedImage();
Assert.Equal(string.Empty, visitorService.CapturedImage);
Assert.Equal(string.Empty, visitorService.CapturedImageName);
}
In the test, the only extra step while setting up the VisitorService class is to initialize the GetCurrentSession field to return null.
visitorService.GetCurrentSession = () => null;
The beauty of this approach is that you only change the VisitorClass. Other classes that depend on it don’t change.
Use the Extract and Override Call
Extract and override call is a refactoring technique that you can use to unit test a static method. The idea is to create a new protected method that will wrap the call to the static method. Then you subclass the original class for testing purposes and override the behavior in the subclass.
Use the following steps to perform Extract and Override Call:
- Perform Extract Method refactoring to move the static method call to a separate, new method.
- Make the method protected and virtual.
- Create a subclass of the original class in your testing project.
- Override the behavior in the new method.
Using the steps above, start by extracting into a new protected method the call to the static method:
public class VisitorService
{
public string CapturedImage { get; set; }
public string CapturedImageName { get; set; }
public void ClearCapturedImage()
{
if (GetSession() == null)
{
CapturedImage = "";
CapturedImageName = "";
}
}
protected virtual AppSession GetSession()
{
return CurrentSession.GetCurrentSession();
}
}
(If you need a visual guide and detail step-by-step on how to perform the Extract Method refactoring fast using Visual Studio, make sure to check the following article.)
Now you can create a subclass in the testing project:
class FakeVisitorService: VisitorService
{
public AppSession FakeSession { get; set; }
protected override AppSession GetSession()
{
return FakeSession;
}
}
And use that class while writing a unit test:
[Fact]
public void ClearCapturedImage_clears_captured_image_when_session_is_null()
{
var visitorService = new FakeVisitorService()
{
CapturedImage = "image.png",
CapturedImageName = "initial name",
FakeSession = null
};
visitorService.ClearCapturedImage();
Assert.Equal(string.Empty, visitorService.CapturedImage);
Assert.Equal(string.Empty, visitorService.CapturedImageName);
}
The advantage of this approach over using a static function is that you don’t expose any new internals to the outside code. The only code that can see the change is the subclass in the testing code.
Note that this approach will only work if the parent is not a static class.
Conclusion
Unit testing is an essential part of software development. By using one of the three approaches outlined in this article, you can quickly test a static method in C#. By doing so, you can ensure that your code delivers the expected results.
Each of the three approaches offers its advantages, so you may want to experiment with different strategies and see what works best for your project.