Setting HttpContext.Current.Session in a unit test
Solution 1:
You can "fake it" by creating a new HttpContext
like this:
http://www.necronet.org/archive/2010/07/28/unit-testing-code-that-uses-httpcontext-current-session.aspx
I've taken that code and put it on an static helper class like so:
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://example.com/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
Or instead of using reflection to construct the new HttpSessionState
instance, you can just attach your HttpSessionStateContainer
to the HttpContext
(as per Brent M. Spell's comment):
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
and then you can call it in your unit tests like:
HttpContext.Current = MockHelper.FakeHttpContext();
Solution 2:
We had to mock HttpContext
by using a HttpContextManager
and calling the factory from within our application as well as the Unit Tests
public class HttpContextManager
{
private static HttpContextBase m_context;
public static HttpContextBase Current
{
get
{
if (m_context != null)
return m_context;
if (HttpContext.Current == null)
throw new InvalidOperationException("HttpContext not available");
return new HttpContextWrapper(HttpContext.Current);
}
}
public static void SetCurrentContext(HttpContextBase context)
{
m_context = context;
}
}
You would then replace any calls to HttpContext.Current
with HttpContextManager.Current
and have access to the same methods. Then when you're testing, you can also access the HttpContextManager
and mock your expectations
This is an example using Moq:
private HttpContextBase GetMockedHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
var urlHelper = new Mock<UrlHelper>();
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var requestContext = new Mock<RequestContext>();
requestContext.Setup(x => x.HttpContext).Returns(context.Object);
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
context.Setup(ctx => ctx.User).Returns(user.Object);
user.Setup(ctx => ctx.Identity).Returns(identity.Object);
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns("test");
request.Setup(req => req.Url).Returns(new Uri("http://www.google.com"));
request.Setup(req => req.RequestContext).Returns(requestContext.Object);
requestContext.Setup(x => x.RouteData).Returns(new RouteData());
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
return context.Object;
}
and then to use it within your unit tests, I call this within my Test Init method
HttpContextManager.SetCurrentContext(GetMockedHttpContext());
you can then, in the above method add the expected results from Session that you're expecting to be available to your web service.
Solution 3:
Milox solution is better than the accepted one IMHO but I had some problems with this implementation when handling urls with querystring.
I made some changes to make it work properly with any urls and to avoid Reflection.
public static HttpContext FakeHttpContext(string url)
{
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}
Solution 4:
I worte something about this a while ago.
Unit Testing HttpContext.Current.Session in MVC3 .NET
Hope it helps.
[TestInitialize]
public void TestSetup()
{
// We need to setup the Current HTTP Context as follows:
// Step 1: Setup the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost/", "");
// Step 2: Setup the HTTP Response
var httpResponce = new HttpResponse(new StringWriter());
// Step 3: Setup the Http Context
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
// Step 4: Assign the Context
HttpContext.Current = httpContext;
}
[TestMethod]
public void BasicTest_Push_Item_Into_Session()
{
// Arrange
var itemValue = "RandomItemValue";
var itemKey = "RandomItemKey";
// Act
HttpContext.Current.Session.Add(itemKey, itemValue);
// Assert
Assert.AreEqual(HttpContext.Current.Session[itemKey], itemValue);
}
Solution 5:
You can try FakeHttpContext:
using (new FakeHttpContext())
{
HttpContext.Current.Session["CustomerId"] = "customer1";
}