Blog of Adrian Anttila, containing my thoughts, comments and questions. RSS Feed


Mocking HttpContext for Unit Testing

One of the recent challenges we were dealing with on the project I'm currently on involved added unit tests to code that was written several years ago and has been modified consistently since then. The biggest problem we found was that the MiddleTier had references to System.Web because several methods took an HttpContext or HttpApplication instance. Some methods simply referenced the static HttpContext.Current property.

Due to the current implementation of the ASP.NET object model, it's not a simple task to instantiate a new HttpApplication or HttpContext and set the properties you require. One of the other guys had spent a day or two on the problem, and was expressing his frustration to me. I recommended using reflection to see if that would get him anywhere, and since he wasn't real familiar with it, we took a few hours and wrote the following code at my desk:

Assembly assembly = Assembly.GetAssembly(typeof(HttpApplication));
Type type = assembly.GetType("System.Web.HttpApplicationFactory", true);             

PropertyInfo propertyInfo = type.GetProperty("ApplicationState", BindingFlags.NonPublic | BindingFlags.Static);
object applicationState = propertyInfo.GetValue(null, new object[0]);         

type.TypeInitializer.Invoke(null, new object[0]);             

ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, Type.DefaultBinder, Type.EmptyTypes, new ParameterModifier[0]);
object applicationFactory = constructorInfo.Invoke(new object[0]);             

FieldInfo fieldInfo = type.GetField("_theApplicationType", BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo.SetValue(applicationFactory, typeof(HttpApplication));

fieldInfo = type.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo.SetValue(applicationFactory, applicationState);             

fieldInfo = type.GetField("_theApplicationFactory", BindingFlags.NonPublic | BindingFlags.Static);
fieldInfo.SetValue(null, applicationFactory);             

using (StringWriter stringWriter = new StringWriter())

    AppDomain.CurrentDomain.SetData(".appPath", Environment.GetEnvironmentVariable("TEMP")); 
    AppDomain.CurrentDomain.SetData(".appVPath", "/");                 

    SimpleWorkerRequest request = new SimpleWorkerRequest(String.Empty, String.Empty, stringWriter); 
    HttpContext context = new HttpContext(request);                 

    Type[] types = new Type[] { typeof(HttpContext) }; 
    MethodInfo methodInfo = type.GetMethod("GetNormalApplicationInstance", BindingFlags.NonPublic | BindingFlags.Instance, Type.DefaultBinder, types, new ParameterModifier[0]);                 

    object[] parameters = new object[] { context }; 
    context.ApplicationInstance = (HttpApplication) methodInfo.Invoke(applicationFactory, parameters);                 

    HttpContext.Current = context;
}

HttpContext.Current.Application.Add("FirstName", "Adrian");
Console.WriteLine("FirstName: {0}", HttpContext.Current.Application["FirstName"]);

The above code creates an HttpApplicationFactory, sets 2 required environment variables, and calls the application factory's GetNormalApplicationInstance method to create an HttpApplication. Once the application has been created, it is assigned to a new HttpContext's ApplicationInstance property. The last 2 lines are there to verify that the data put into the current HttpContext can be read back.

Most of this code wouldn't be possible without Reflector, which helped us quite a bit.

I'd also like to point out that running the above code probably requires the FullTrust permission, and I wouldn't recommend running it in a production environment with rigorous testing and performance tweaking. For us, we just needed this functionality for unit testing, nothing more.

 
Posted by Adrian Anttila | 4 Comments | Trackback Url | Bookmark with:        
Tags:

Links to this Post

Comments

Tuesday, 14 Aug 2007 02:31 by Re: Mocking HttpContext for Unit Testing
You might want to take a look at Phil Haack's HttpSimulator, which handles HttpContext but includes some tricky things like MapPath, Session, and Application variables: http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx

Friday, 14 Sep 2007 08:52 by Re: Mocking HttpContext for Unit Testing
Beautiful -- its a little verbose, but it does exactly what i need to write my tests.

Thursday, 25 Oct 2007 12:38 by Excellent Post
I've been looking for an effective way to utilize the HttpContxt ApplicationState in my unit tests. This was just what I was looking for!

Monday, 7 Apr 2008 04:56 by Awesome post
just got what we wanted :) Thanks

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation