Is there a way to mock/fake the session object in ASP.Net Web forms when creating unit tests?
I am currently storing user details in a session variable which is accessed by my business logic.
When testing my business logic in isolation, the session is not available. This seems to indicate a bad design (though I'm not sure). Should the business logic layer be accessing session variables in the first place?
If so, then how would I go about swapping the user details with a fake object for testing?
You can do it with essentially 4 lines of code. Although this doesn't speak to the previous comment of moving session out of your business logic layer, sometimes you might need to do this anyway if you're working with legacy code that is heavily coupled to the session (my scenario).
The namespaces:
using System.Web;
using System.IO;
using System.Web.Hosting;
using System.Web.SessionState;
The code:
HttpWorkerRequest _wr = new SimpleWorkerRequest(
"/dummyWorkerRequest", @"c:\inetpub\wwwroot\dummy",
"default.aspx", null, new StringWriter());
HttpContext.Current = new HttpContext(_wr);
var sessionContainer = new HttpSessionStateContainer(
"id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect, SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
HttpContext.Current, sessionContainer);
You can then refer to the session without getting a NullReferenceException error:
HttpContext.Current.Session.Add("mySessionKey", 1);
This is a combination of code I compiled from the articles below:
In ASP.NET, you can't create a Test Double of HttpSessionState because it is sealed
. Yes, this is bad design on the part of the original designers of ASP.NET, but there's not a lot to do about it.
This is one of many reasons why TDD'ers and other SOLID practictioners have largely abandonded ASP.NET in favor of ASP.NET MVC and other, more testable frameworks. In ASP.NET MVC, the HTTP session is modelled by the abstract HttpSessionStateBase class.
You could take a similar approach and let your objects work on an abstract session, and then wrap the real HttpSessionState class when you are running in the ASP.NET environment. Depending on circumstances, you may even be able to reuse the types from System.Web.Abstractions, but if not, you can define your own.
In any case, your business logic is your Domain Model and it should be modeled independently of any particular run-time technology, so I would say that it shouldn't be accessing the session object in the first place.
If you absolutely need to use Test Doubles for unit tets involving HttpSessionState, this is still possible with certain invasive dynamic mocks, such as TypeMock or Moles, althought these carry plenty of disadvantages as well (see this comparison of dynamic mocks).
Your instincts are correct---you shouldn't be accessing pieces of the ASP.NET framework from your business logic. This would include Session.
To answer your first question, you can mock static classes using a product like Typemock Isolator, but you'll be better off if you refactor your code to wrap access to Session in an interface (i.e., IHttpSession.) You can then mock IHttpSession.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With