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?
We had to mock
HttpContext
by using aHttpContextManager
and calling the factory from within our application as well as the Unit TestsYou would then replace any calls to
HttpContext.Current
withHttpContextManager.Current
and have access to the same methods. Then when you're testing, you can also access theHttpContextManager
and mock your expectationsThis is an example using Moq:
and then to use it within your unit tests, I call this within my Test Init method
you can then, in the above method add the expected results from Session that you're expecting to be available to your web service.
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:
Or instead of using reflection to construct the new
HttpSessionState
instance, you can just attach yourHttpSessionStateContainer
to theHttpContext
(as per Brent M. Spell's comment):and then you can call it in your unit tests like:
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
and then append
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.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.
In asp.net Core / MVC 6 rc2 you can set the
HttpContext
rc 1 was
https://stackoverflow.com/a/34022964/516748
Consider using
Moq
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 toWhen called from test, _customObject uses alternative store (DB or cloud key value[ http://www.kvstore.io/] )
But when called from the real application,
_customObject
usesSession
.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