Setting HttpContext.Current.Session in a unit test

C#Web ServicesUnit TestingHttpcontext

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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 - C#

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 - C#

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 - C#

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 - C#

If you're using the MVC framework, this should work. I used Milox's FakeHttpContext and added a few additional lines of code. The idea came from this post:

http://codepaste.net/p269t8

This seems to work in MVC 5. I haven't tried this in earlier versions of MVC.

HttpContext.Current = MockHttpContext.FakeHttpContext();

var wrapper = new HttpContextWrapper(HttpContext.Current);

MyController controller = new MyController();
controller.ControllerContext = new ControllerContext(wrapper, new RouteData(), controller);

string result = controller.MyMethod();

Solution 6 - C#

You can try FakeHttpContext:

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}

Solution 7 - C#

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>();

Solution 8 - C#

The answer that worked with me is what @Anthony had written, but you have to add another line which is

    request.SetupGet(req => req.Headers).Returns(new NameValueCollection());

so you can use this:

HttpContextFactory.Current.Request.Headers.Add(key, value);

Solution 9 - C#

Try this:

        // MockHttpSession Setup
        var session = new MockHttpSession();

        // MockHttpRequest Setup - mock AJAX request
        var httpRequest = new Mock<HttpRequestBase>();
        
        // Setup this part of the HTTP request for AJAX calls
        httpRequest.Setup(req => req["X-Requested-With"]).Returns("XMLHttpRequest");

        // MockHttpContextBase Setup - mock request, cache, and session
        var httpContext = new Mock<HttpContextBase>();
        httpContext.Setup(ctx => ctx.Request).Returns(httpRequest.Object);
        httpContext.Setup(ctx => ctx.Cache).Returns(HttpRuntime.Cache);
        httpContext.Setup(ctx => ctx.Session).Returns(session);

        // MockHttpContext for cache
        var contextRequest = new HttpRequest("", "http://localhost/", "");
        var contextResponse = new HttpResponse(new StringWriter());
        HttpContext.Current = new HttpContext(contextRequest, contextResponse);

        // MockControllerContext Setup
        var context = new Mock<ControllerContext>();
        context.Setup(ctx => ctx.HttpContext).Returns(httpContext.Object);

        //TODO: Create new controller here
        //      Set controller's ControllerContext to context.Object

And Add the class:

public class MockHttpSession : HttpSessionStateBase
{
    Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>();
    public override object this[string name]
    {
        get
        {
            return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null;
        }
        set
        {
            _sessionDictionary[name] = value;
        }
    }

    public override void Abandon()
    {
        var keys = new List<string>();

        foreach (var kvp in _sessionDictionary)
        {
            keys.Add(kvp.Key);
        }

        foreach (var key in keys)
        {
            _sessionDictionary.Remove(key);
        }
    }

    public override void Clear()
    {
        var keys = new List<string>();

        foreach (var kvp in _sessionDictionary)
        {
            keys.Add(kvp.Key);
        }

        foreach(var key in keys)
        {
            _sessionDictionary.Remove(key);
        }
    }
}

This will allow you to test with both session and cache.

Solution 10 - C#

I was looking for something a little less invasive than the options mentioned above. In the end I came up with a cheesy solution, but it might get some folks moving a little faster.

First I created a TestSession class:

class TestSession : ISession
{

    public TestSession()
    {
        Values = new Dictionary<string, byte[]>();
    }

    public string Id
    {
        get
        {
            return "session_id";
        }
    }

    public bool IsAvailable
    {
        get
        {
            return true;
        }
    }

    public IEnumerable<string> Keys
    {
        get { return Values.Keys; }
    }

    public Dictionary<string, byte[]> Values { get; set; }

    public void Clear()
    {
        Values.Clear();
    }

    public Task CommitAsync()
    {
        throw new NotImplementedException();
    }

    public Task LoadAsync()
    {
        throw new NotImplementedException();
    }

    public void Remove(string key)
    {
        Values.Remove(key);
    }

    public void Set(string key, byte[] value)
    {
        if (Values.ContainsKey(key))
        {
            Remove(key);
        }
        Values.Add(key, value);
    }

    public bool TryGetValue(string key, out byte[] value)
    {
        if (Values.ContainsKey(key))
        {
            value = Values[key];
            return true;
        }
        value = new byte[0];
        return false;
    }
}

Then I added an optional parameter to my controller's constructor. If the parameter is present, use it for session manipulation. Otherwise, use the HttpContext.Session:

class MyController
{

    private readonly ISession _session;

    public MyController(ISession session = null)
    {
        _session = session;
    }


    public IActionResult Action1()
    {
        Session().SetString("Key", "Value");
        View();
    }

    public IActionResult Action2()
    {
        ViewBag.Key = Session().GetString("Key");
        View();
    }

    private ISession Session()
    {
        return _session ?? HttpContext.Session;
    }
}

Now I can inject my TestSession into the controller:

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}

Solution 11 - C#

The answer @Ro Hit gave helped me a lot, but I was missing the user credentials because I had to fake a user for authentication unit testing. Hence, let me describe how I solved it.

According to this, if you add the method

    // using System.Security.Principal;
    GenericPrincipal FakeUser(string userName)
    {
        var fakeIdentity = new GenericIdentity(userName);
        var principal = new GenericPrincipal(fakeIdentity, null);
        return principal;
    }

and then append

    HttpContext.Current.User = FakeUser("myDomain\\myUser");

to the last line of the TestSetup method you're done, the user credentials are added and ready to be used for authentication testing.

I also noticed that there are other parts in HttpContext you might require, such as the .MapPath() method. There is a FakeHttpContext available, which is described here and can be installed via NuGet.

Solution 12 - C#

I found the following simple solution for specifying a user in the HttpContext: https://forums.asp.net/post/5828182.aspx

Solution 13 - C#

Never mock.. never! The solution is pretty simple. Why fake such a beautiful creation like HttpContext?

Push the session down! (Just this line is enough for most of us to understand but explained in detail below)

(string)HttpContext.Current.Session["CustomerId"]; is how we access it now. Change this to

_customObject.SessionProperty("CustomerId")

When called from test, _customObject uses alternative store (DB or cloud key value[ http://www.kvstore.io/] )

But when called from the real application, _customObject uses Session.

how is this done? well... Dependency Injection!

So test can set the session(underground) and then call the application method as if it knows nothing about the session. Then test secretly checks if the application code correctly updated the session. Or if the application behaves based on the session value set by the test.

Actually, we did end up mocking even though I said: "never mock". Becuase we couldn't help but slip to the next rule, "mock where it hurts the least!". Mocking huge HttpContext or mocking a tiny session, which hurts the least? don't ask me where these rules came from. Let us just say common sense. Here is an interesting read on not mocking as unit test can kills us

Solution 14 - C#

Try this way..

public static HttpContext getCurrentSession()
  {
        HttpContext.Current = new HttpContext(new HttpRequest("", ConfigurationManager.AppSettings["UnitTestSessionURL"], ""), new HttpResponse(new System.IO.StringWriter()));
        System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
        HttpContext.Current, new HttpSessionStateContainer("", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 20000, true,
        HttpCookieMode.UseCookies, SessionStateMode.InProc, false));
        return HttpContext.Current;
  }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDaveBView Question on Stackoverflow
Solution 1 - C#MiloxView Answer on Stackoverflow
Solution 2 - C#Anthony ShawView Answer on Stackoverflow
Solution 3 - C#giamminView Answer on Stackoverflow
Solution 4 - C#Rohit LView Answer on Stackoverflow
Solution 5 - C#NimblejoeView Answer on Stackoverflow
Solution 6 - C#vADView Answer on Stackoverflow
Solution 7 - C#KCDView Answer on Stackoverflow
Solution 8 - C#yzicusView Answer on Stackoverflow
Solution 9 - C#Isaac AlvaradoView Answer on Stackoverflow
Solution 10 - C#Chris HansonView Answer on Stackoverflow
Solution 11 - C#MattView Answer on Stackoverflow
Solution 12 - C#Lars LadegaardView Answer on Stackoverflow
Solution 13 - C#Blue CloudsView Answer on Stackoverflow
Solution 14 - C#Ranjan SinghView Answer on Stackoverflow