With the release of .net 3.5 SP1 we now have a new assembly called System.Web.Abstractions. In this namespace you will find several new classes:
Many of you are familiar with ASP.NET MVC and already know what these classes are for, but for those that are not, these are wrapper classes around most of the static HttpContext rooted classes in asp.net. These classes allow for you to Mock out the HttpContext, Request, Response, etc… They also provide base classes which allow for you to create your own custom implementation or stubs, and override different methods as you see fit.
What most people may not realize is that these classes aren’t limited to being used in your ASP.NET MVC applications, and they don’t require you to do anything special other than to be a bit more rigorous about retrieving your HttpContext objects.
So, lets say that I want to start using the HttpContextBase in order to wrap my HttpContext so that I can make my application easier to test. First I would declare a class which I am going to start using called HttpContextFactory:
public static class HttpContextFactory { [ThreadStatic] private static HttpContextBase mockHttpContext; public static void SetHttpContext(HttpContextBase httpContextBase) { mockHttpContext = httpContextBase; } public static void ResetHttpContext() { mockHttpContext = null; } public static HttpContextBase GetHttpContext() { if (mockHttpContext != null) { return mockHttpContext; } if (HttpContext.Current != null) { return new HttpContextWrapper(HttpContext.Current); } return null; } }
This class has a method called "GetHttpContext" which returns an HttpContextBase. As you can see, we also have a "SetHttpContext" and "ResetHttpContext". Inside of your application, if you wanted to use the HttpContext, then you could access it like this:
HttpContextBase httpContextBase = HttpContextFactory.GetHttpContext(); var page = httpContextBase.Handler as Page;
And all is well. The httpContextBase should look exactly like the normal HttpContext class when you interact with it. The difference now is that when you are writing your tests, you can create a mock httpContextBase and use that to test your UI components:
var mockHttpContext = new Mock<HttpContextBase>(MockBehavior.Strict); var page = new Page(); mockHttpContext.Expect(h => h.Handler).Returns(page);
Here we are using Moq to mock out our http context and then setting an expectation when the "Handler" property is accessed to return a page that we have instantiated. If you aren’t familiar with using mocks, then you might check out their quick-start.
Here we are actually declaring a new Page class and using it, which means that we are still going to be quite limited in what we can do since we aren’t running inside of an actual request. This can help a lot though if you have created a base page class which some of your custom controls depend on, or if you have methods which need to write out to the HttpContext response and you need to intercept their calls.
I will now show you a fake test which will mimic some component using our http context factory and using it to write our to the response:
[Test] public void FakeTest() { // create our mocks var mockHttpContext = new Mock<HttpContextBase>(MockBehavior.Strict); var mockHttpResponse = new Mock<HttpResponseBase>(); //set our expectations string output = ""; mockHttpContext.Expect(mhc => mhc.Response).Returns(mockHttpResponse.Object); mockHttpResponse.Expect(mhr => mhr.Write(It.IsAny<string>())).Callback<string>(s => output += s); //set the mock context HttpContextFactory.SetHttpContext(mockHttpContext.Object); // This would be an external component writing to the response HttpContextBase httpContext = HttpContextFactory.GetHttpContext(); httpContext.Response.Write("Write test!"); httpContext.Response.Write("Write Some More!"); //assert what was written Assert.AreEqual("Write test!Write Some More!", output); }
Not too insanely difficult, but not easy either. We are creating the mocks, then setting some expectations on what will be called and what we return from those calls. The second expectation line has a Callback which simply concatenates what is passed to "Response.Write" into a local variable called "output". Then we just set our mock context in the factory.
Next there are three lines where we pull the HttpContext out and call "Response.Write" and then we can check what was written out. Normally we would not be calling these lines directly, instead they would be inside of a component that we are testing. Next we just assert our output against what the component should have written.
There is a lot of overhead in what we are doing here, but we could easily abstract away a good amount of this into helper classes:
[Test] public void FakeTest() { Mock<HttpResponseBase> mockHttpResponse = GetMockHttpResponse(); string output = ""; mockHttpResponse.Expect(mhr => mhr.Write(It.IsAny<string>())).Callback<string>(s => output += s); var testComponent = new TestComponent(); testComponent.Render(); //assert what was written Assert.AreEqual("Test component output!", output); }
So there! Our "GetMockHttpResponse" method would look like this:
public Mock<HttpResponseBase> GetMockHttpResponse() { // create our mocks var mockHttpContext = new Mock<HttpContextBase>(MockBehavior.Strict); var mockHttpResponse = new Mock<HttpResponseBase>(); //set our expectations mockHttpContext.Expect(mhc => mhc.Response).Returns(mockHttpResponse.Object); //set the mock context HttpContextFactory.SetHttpContext(mockHttpContext.Object); return mockHttpResponse; }
I hope this can help you get started down the road of testing custom components in asp.net web applications by leveraging the System.Web.Abstractions assembly. Thanks!
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.
What, no CacheWrapper? Bastards.
@Will It would have been cool if they could have done it, but the Cache is actually one of the easier things to wrap in an interface (assuming you only need to support a subset of actions).
I’m wondering if you ever really needed to use the ThreadStaticAttribute in this case? It seems that if your test function relies on their being different HttpContexts in other threads (implying a multithreaded test) then your test function needs to set those up within each thread. Not easily done.
I’m thinking I’m going to drop the attribute, if anything so I’m not using thread-locale storage in production when it is not before necessary.
Thanks for the article, very helpful.
@fshwiet Unless I am missing something, removing the ThreadStaticAttribute will cause every currently executing thread to get the same instance of HttpContext…which you don’t want.
In this case, the HttpContext factory would not only be used during testing, but during program execution as well. In an ideal case you would be using an IoC container and wouldn’t have to worry about setting up a factory like this.
My thought was that in production no one should call SetHttpcontext, so in that case ThreadStaticAttribute is not necessary. For test code it is only useful if you have a multithreaded test. I can’t think of a case where I would be testing code that is dependent on the HttpContext of another thread.
@fschwiet Doh, you’re right. Yeah, you can remove it. I think for some reason I thought I was caching the context in that variable. Which would have been bad since ThreadStatic is bad inside of http requests.
Justin, you can use HttpRuntime.Cache instead of httpcontext.current.cache. like so:
var cache = HttpRuntime.Cache;
var http = HttpContextFactory.Current;
A.CallTo(() => http.Cache).Returns(cache);