Mocking the HttpContext Session object in ASP.NET Core 2.0

Within our web application, we are using the HttpContext.Session object to store certain items of information. Although we make minimal use of this object (it is after all, global data), there are times when it just makes sense to store certain kinds of information in the session. For example, when the user logs into the application, we grab their email address and store this in the session. This obviously won’t change and so is a prime candidate for storing within the session.

All of our services require that the user’s email address is passed as parameter so we can determine the user making the request. Our functions therefore retrieve the email address from session storage. This works great, but led to a problem when I came to try unit testing the functions as I was unable to access session storage from my unit tests. After a bit of trial and error I came up with the following solution. Googling the problem revealed that there are several ways in which this can be achieved. I didn’t want to use a mocking framework, as I wanted to keep the unit tests as small and simple as possible. Although adding a mocking framework would have given me a lot more functionality to play with, I was only interested in mocking the HttpContext.Sessionobject. The solution I have used is a vanilla approach that doesn’t use any external frameworks, thus making it possible for anyone to implement it.

First off, I created a class that implemented the ISession interface. This is the same interface that the HttpContext.Session object implements.

Hide Expand

Image for post
Image for post

Copy Code

public class MockHttpSession : ISession
{
readonly Dictionary<string, object> _sessionStorage = new Dictionary<string, object>();
string ISession.Id => throw new NotImplementedException();
bool ISession.IsAvailable => throw new NotImplementedException();
IEnumerable<string> ISession.Keys => _sessionStorage.Keys;
void ISession.Clear()
{
_sessionStorage.Clear();
}
Task ISession.CommitAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
Task ISession.LoadAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
void ISession.Remove(string key)
{
_sessionStorage.Remove(key);
}
void ISession.Set(string key, byte[] value)
{
_sessionStorage[key] = Encoding.UTF8.GetString(value);
}
bool ISession.TryGetValue(string key, out byte[] value)
{
if (_sessionStorage[key] != null)
{
value = Encoding.ASCII.GetBytes(_sessionStorage[key].ToString());
return true;
}
value = null;
return false;
}
}

My functions have an optional parameter which takes an instance of an ISession object. If one is not passed as an argument then the function simply uses HttpSession.Session instead.

Hide Copy Code

public void MyFunction(ISession context = null)
{
if (context == null)
{
context = HttpContext.Session;
}
string email = context.Get("UserEmail");
//rest of the function code goes here
}

Then in my unit tests I create an instance of the mock Httpcontext.Session class from above and pass this as an argument to the functions I wish to unit test. In the example below I am unit testing a ViewComponent that retrieves the user’s email from the HttpContext.Session object.

Hide Copy Code

[TestMethod]
public async Task InvokeAsyncTest()
{
MainMenuViewComponent component = new MainMenuViewComponent();
var mockContext = MockHttpContext();
var result = await component.InvokeAsync(mockContext); Assert.IsNotNull(result);
}
private static ISession MockHttpContext()
{
MockHttpSession httpcontext = new MockHttpSession();
httpcontext.Set<string>("UserEmail", "unittest@mycompany.com");
return httpcontext;
}

I have now implemented this same pattern for all my ViewComponent’s, and it works absolutely perfectly. I can easily and simply unit test any code that makes use of the HttpContext.Session object without a problem.

A father, cyclist, vegetarian, atheist, geek and multiple award winning technical author. Loves real ale, fine wine and good music. All round decent chap.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store