Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Unit testing HttpContext.Current outside of a Controller

0.00/5 (No votes)
5 Jan 2016 1  
Article discussing how to unit test HttpContext.Current outside of a controller

How well do you test your code? Maybe you're a TDD ninja. Perhaps you're just trying to develop the habit of unit testing. You're trying to break away from mocking up an HTML page just for testing. Trouble is, you've fallen at the first hurdle. You're writing a test against code that calls HttpContext. Well, HttpContext.Current to be exact. And the test fails. How frustrating is that? It's not even your code! In this post I'll walk you through a simple technique to make HttpContext.Current testable.

We need to access HttpContext in all sorts of situations when developing MVC applications. Most of the time we access it within a Controller, but sometimes we need it outside of that. We're looking to write unit tests for the majority of our code. This means that we need to test code that calls HttpContext.Current. As soon as we do that, we get the dreaded NullReferenceException and our test fails. The way we get around that is to mock it.

So what is mocking?

Mocking is a technique that allows us to swap out the object we can't test with one that we can. It's an important part of unit testing. Why? Because it allows us to ignore code that's outside of the class we're testing (the code under test). The key to unit testing is to test the smallest piece of code that you can. If the code calls other parts of the system or third party classes, we want to ignore those. We do that by mocking them. Whenever the code calls an object outside of our code under test, we mock it. In effect, we ask the mocking framework to give us an object of the same type instead. Our test code is then in control of what happens with that object.

The problem is, unless we use TypeMock Isolator, we can't mock HttpContext. Why? Because it's a concrete type. Most mocking frameworks (here I'm thinking MOQ, Rhino Mocks and NSubstitute) only allow us to mock abstract classes, interfaces and virtual methods. This encourages us to write code with loose coupling. But it can make testing tricky.

Show me the code!

One solution to our problem is to wrap HttpContext.Current within a class that we can mock. We do this by using HttpContextWrapper, which is of type HttpContextBase, an abstract class. This means we can mock it. So we can test it. Hurrah! Here's a class we can use:

using System.Web;

public interface IContextWrapper
{
    HttpContextBase Current { get; }
    HttpRequestBase Request { get; }
    HttpResponseBase Response { get; }
    HttpServerUtilityBase Server { get; }
}

public class ContextWrapper : IContextWrapper
{
    public HttpContextBase Current
    {
        get
        {
            var httpContext = HttpContext.Current;
            return httpContext == null ? null : new HttpContextWrapper(httpContext);
        }
    }

    public HttpRequestBase Request
    {
        get { return Current.Request; }
    }

    public HttpResponseBase Response
    {
        get { return Current.Response; }
    }

    public HttpServerUtilityBase Server
    {
        get { return Current.Server; }
    }
}

I'm exposing a few common properties (Request, Response and Server) but they're optional. As long as we expose Current as HttpContextBase then we're fine. Whenever we need to use HttpContext.Current, we can inject our wrapper class instead. This allows us to do things like getting the username of the current logged in user (for example):

public class AddUserViewModelFactory : IViewModelFactory<AddUserViewModel>
{
    private readonly IContextWrapper contextWrapper
    public AddUserViewModelFactory(IContextWrapper contextWrapper)
    {
        this.contextWrapper = contextWrapper;
    }

    public AddUserViewModel Create()
    {
        var currentUser = contextWrapper.Current.User.Identity.Name;
    }
}

View original article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here