Setting HttpContext.Current.Session in a unit test
Solution 1
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 2
You can "fake it" by creating a new HttpContext
like this:
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 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
In asp.net Core / MVC 6 rc2 you can set the HttpContext
var SomeController controller = new SomeController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
rc 1 was
var SomeController controller = new SomeController();
controller.ActionContext = new ActionContext();
controller.ActionContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
https://stackoverflow.com/a/34022964/516748
Consider using Moq
new Mock<ISession>();
Related videos on Youtube
DaveB
Updated on May 13, 2022Comments
-
DaveB about 2 years
I have a web service I am trying to unit test. In the service it pulls several values from the
HttpContext
like so:m_password = (string)HttpContext.Current.Session["CustomerId"]; m_userID = (string)HttpContext.Current.Session["CustomerUrl"];
in the unit test I am creating the context using a simple worker request, like so:
SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter()); HttpContext context = new HttpContext(request); HttpContext.Current = context;
However, whenever I try to set the values of
HttpContext.Current.Session
HttpContext.Current.Session["CustomerId"] = "customer1"; HttpContext.Current.Session["CustomerUrl"] = "customer1Url";
I get null reference exception that says
HttpContext.Current.Session
is null.Is there any way to initialize the current session within the unit test?
-
Raj Ranjhan about 12 yearsDid you try this method?
-
jrummell about 12 yearsUse HttpContextBase if you can.
-
-
Sean Glover over 11 yearsI like this answer better than the accepted one because changing your production code to support your testing activities is bad practice. Granted, your production code should abstract out 3rd party namespaces like this, but when you're working with legacy code you don't always have this control or the luxury to re-factor.
-
knocte over 11 yearsbut this doesn't use SimpleWorkerRequest
-
Anthony Shaw over 11 yearshe was trying to mock out the HttpContext so that his SimpleWorkerRequest would have access to the values from the HttpContext, he would use the HttpContextFactory within his service
-
Brent M. Spell over 11 yearsYou don't have to use reflection to construct the new HttpSessionState instance. You can just attach your HttpSessionStateContainer to the HttpContext using SessionStateUtility.AddHttpSessionStateToContext.
-
Litera almost 11 yearsIs it intentional that the backing field m_context is only returned for a mock context (when set via SetCurrentContext) and that for the real HttpContext, a wrapper is created for every call to Current?
-
Anthony Shaw almost 11 yearsYes it is. m_context is of type HttpContextBase and returning HttpContextWrapper returns HttpContextBase with the Current HttpContext
-
Juan Jimenez almost 11 yearsMockHelper is just the name of the class where the static method is, you can use whatever name you prefer.
-
Joe about 10 yearsI've tried implementing your answer but the Session is still null. Would you please take a look at my Post stackoverflow.com/questions/23586765/…. Thank you
-
Yuck over 9 years
Server.MapPath()
won't work if you use this either. -
Steven de Salas about 9 yearsAny ideas how to write to
HttpRequest.InputStream
using this approach? -
user1713059 about 9 yearsI think the class should not be called a factory but something else, maybe
HttpContextSource
because it does not create new objects. -
Professor of programming over 8 years
HttpContextManager
would be a better name thanHttpContextSource
but I agreeHttpContextFactory
is misleading. -
Amit Kumar over 8 years@AnthonyShaw: I have implemented your code, But still It is giving
HttpContext.current
is null in Mvc Action. -
Omar.Ebrahim over 8 yearsThis answer worked after the accepted answer didn't. Thank you :)
-
drzaus almost 8 yearsI'm getting an empty
HttpContext.Current.Request.ApplicationPath
, which may be whyServer.MapPath()
doesn't work @Yuck? Any idea how to set it? -
John Henckel almost 8 yearsI tried to use the
FakeHttpContext
, but I had to abandon it, because it cannot set the request headers. So I had to use theHttpContextManager
instead. -
KyleMit over 7 yearsThis allows you to fake
httpContext.Session
, any idea how to do the same forhttpContext.Application
? -
Beanwah over 7 yearsWorks great and very simple to use
-
CodeNotFound almost 7 yearsI really like your solution. KISS => Keep It Simple and Stupid ;-)
-
Rhyous about 6 yearsThe link is broken so maybe put the code here next time.
-
mggSoft almost 6 yearsWorks so fine and simple... Thanks!
-
Phil about 5 yearsthis is pretty old, but i use in some of my code UrlHelper uHelper = new UrlHelper(_context.Request.RequestContext); , and then i urn uHelper.Content(...) on some urls. This gives me a null exception when i use your example for tests. How can i edit this so that the urlhelper made from this context works?
-
Luis Gouveia about 3 yearsUnfortunately incompatible with .NET Core
-
vAD about 3 years@LuisGouveia, does .NET Core have such an issue at all?