Setting HttpContext.Current.Session in a unit test

190,355

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:

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 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>();
Share:
190,355

Related videos on Youtube

DaveB
Author by

DaveB

Updated on May 13, 2022

Comments

  • DaveB
    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?

  • Sean Glover
    Sean Glover over 11 years
    I 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
    knocte over 11 years
    but this doesn't use SimpleWorkerRequest
  • Anthony Shaw
    Anthony Shaw over 11 years
    he 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
    Brent M. Spell over 11 years
    You 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
    Litera almost 11 years
    Is 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
    Anthony Shaw almost 11 years
    Yes it is. m_context is of type HttpContextBase and returning HttpContextWrapper returns HttpContextBase with the Current HttpContext
  • Juan Jimenez
    Juan Jimenez almost 11 years
    MockHelper is just the name of the class where the static method is, you can use whatever name you prefer.
  • Joe
    Joe about 10 years
    I'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
    Yuck over 9 years
    Server.MapPath() won't work if you use this either.
  • Steven de Salas
    Steven de Salas about 9 years
    Any ideas how to write to HttpRequest.InputStream using this approach?
  • user1713059
    user1713059 about 9 years
    I think the class should not be called a factory but something else, maybe HttpContextSource because it does not create new objects.
  • Professor of programming
    Professor of programming over 8 years
    HttpContextManager would be a better name than HttpContextSource but I agree HttpContextFactory is misleading.
  • Amit Kumar
    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
    Omar.Ebrahim over 8 years
    This answer worked after the accepted answer didn't. Thank you :)
  • drzaus
    drzaus almost 8 years
    I'm getting an empty HttpContext.Current.Request.ApplicationPath, which may be why Server.MapPath() doesn't work @Yuck? Any idea how to set it?
  • John Henckel
    John Henckel almost 8 years
    I tried to use the FakeHttpContext, but I had to abandon it, because it cannot set the request headers. So I had to use the HttpContextManager instead.
  • KyleMit
    KyleMit over 7 years
    This allows you to fake httpContext.Session, any idea how to do the same for httpContext.Application?
  • Beanwah
    Beanwah over 7 years
    Works great and very simple to use
  • CodeNotFound
    CodeNotFound almost 7 years
    I really like your solution. KISS => Keep It Simple and Stupid ;-)
  • Rhyous
    Rhyous about 6 years
    The link is broken so maybe put the code here next time.
  • mggSoft
    mggSoft almost 6 years
    Works so fine and simple... Thanks!
  • Phil
    Phil about 5 years
    this 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
    Luis Gouveia about 3 years
    Unfortunately incompatible with .NET Core
  • vAD
    vAD about 3 years
    @LuisGouveia, does .NET Core have such an issue at all?