27

I've been going through the process of cleaning up our controller code to make each action as testable. Generally speaking, this hasn't been too difficult--where we have opportunity to use a fixed object, like say FormsAuthentication, we generally introduce some form of wrapper as appropriate and be on our merry way.

For reasons not particularly germaine to this conversation, when it came to dealing with usage of HttpContext, we decided to use the newly created HttpContextWrapper class rather than inventing something homegrown. One thing we did introduce was the ability to swap in a HttpContextWrapper (like say, for unit testing). This was wholly inspired by the way Oren Eini handles unit testing with DateTimes (see article, a pattern we also use)

public static class FooHttpContext
{
    public static Func<HttpContextWrapper> Current = () 
         => new HttpContextWrapper(HttpContext.Current);

    public static void Reset()
    {
        Current = () => new HttpContextWrapper(HttpContext.Current);
    }
}

Nothing particularly fancy. And it works just fine in our controller code. The kicker came when we go to write unit tests. We're using Moq as our mocking framework, but alas

var context = new Mock<HttpContextWrapper>() 

breaks since HttpContextWrapper doesn't have a parameterless ctor. And what does it take as a ctor parameter? A HttpContext object. So I find myself in a catch 22.

I'm using the prescribed way to decouple HttpContext--but I can't mock a value in because the original HttpContext object was sealed and therefore difficult to test. I can map HttpContextBase, which both derive from--but that doesn't really get me what I'm after. Am I just missing the point somewhere with regard to HttpContextWrapper?

Edit to clarify intent

We found ways to solve the problem--but I guess the ultimate question we're walking away with is what value HttpContextWrapper brings to the table? I don't doubt somewhere someone totally had an a-ha! moment with it, but it just doesn't come to me. Most postings I see here discuss it in terms of testability--but my own experience has led me to believe that it didn't bring much in that context. Unless we're doing it wrong. (Wholly possible).

Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
bakasan
  • 2,262
  • 2
  • 26
  • 33

3 Answers3

35

This blog post explains it pretty well:

http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext

The point is that 'vintage' HttpContext does not implement HttpContextBase, and isn't virtual, and therefore cannot be Mocked. HttpContextBase was introduced in 3.5 as a mockable alternative. But there's still the problem that vintage HttpContext doesn't implement HttpContextBase.

So HttpContextWrapper is a handy wrapper class (or 'kludge') that does implement HttpContextBase, and can be used when injecting a 'real' HttpContext using IOC, usually with a factory method like this: () => new HttpContextWrapper(HttpContext.Current)

codeulike
  • 22,514
  • 29
  • 120
  • 167
  • 4
    And you would set up the registration in Unity as follows; `var container = new UnityContainer(); container.RegisterType(new InjectionConstructor(HttpContext.Current));` – Stuart Hallows Nov 03 '12 at 10:57
34

You should be using the abstract HttpContextBase which is much easier to mock instead of HttpContextWrapper.

public static Func<HttpContextBase> Current = 
    () => new HttpContextWrapper(HttpContext.Current);

And in your unit test:

SomeClass.Current = MockHttpContextBase(); // Sorry I don't know the syntax for Moq
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • You're absolutely right--and what we ultimately did...but my question more I guess was about what Wrapper really brings to the table--let me edit for clarity. – bakasan Mar 09 '10 at 09:11
  • 1
    `HttpContextWrapper` is not meant to be an abstraction but a concrete implementation of `HttpContextBase`. I guess it's value is that it hides some static and internal methods of `HttpContext`. – Darin Dimitrov Mar 09 '10 at 09:18
  • 9
    `HttpContextWrapper` implements the (mockable) `HttpContextBase` by forwarding calls to the ASP.NET-vintage `HttpContext`. It's a wart to get around the fact that `HttpContext` isn't mockable. – Roger Lipscombe Mar 09 '10 at 09:22
  • I guess I just question using the wrapper vs just using the base--I presumed it was more of an abstraction than a concrete mechanism given it lives inside System.Web.Abstractions. Would still love to hear about any a-ha! moments where the wrapper was the absolute silver bullet, but otherwise I'll mark this the answer. – bakasan Mar 09 '10 at 16:05
0

One real world example other than testing. Besides mocking I stumbled upon a peculiar issue that the wrapper class really helped me solve. We have an application in Azure and we have control only on the application. It sits behind a reverse proxy which changes the host header of the incoming requests and sends the original host in a custom header. The application relies on the host header in order to build dynamic links, validate redirects etc. so we needed a way to substitute the host that was set in the HttpContext.HttpRequests.Url property. Since we exposed the HttpContext only as HttpContextBaase using the wrapper everywhere in the app we were able to create a class that inherits HttpContextWrapper and overrides the Request and then returns object that inherits from the RequestWrapper and overrides the Url property. So in the end the application replaced the host in the url that ASP.NET used for the context with the custom host from the custom header the reverse proxy has set. There was no other way from within the app to do that other than manually searching through the code where HttpContext.Request.Url is used and applying fix.

todorm
  • 474
  • 4
  • 6