How do I mock/fake the session object in ASP.Net Web forms?

后端 未结 5 1057
终归单人心
终归单人心 2020-12-09 09:41

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 acce

相关标签:
5条回答
  • 2020-12-09 10:21

    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:

    • Unit testing with HttpContext
    • Faking a current HttpContext
    • SimpleWorkerRequest, HttpContext, and session state
    • Unit testing code that uses HttpContext.Current.Session
    0 讨论(0)
  • 2020-12-09 10:22

    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).

    0 讨论(0)
  • 2020-12-09 10:25

    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.

    0 讨论(0)
  • 2020-12-09 10:28

    One approach is to pass a lambda expression to your code that takes a string (or some other object) as input, and uses it to set either the Session object or a test container.

    However, as others have said, it's a good idea to move access to the Session object out of your BLL.

    0 讨论(0)
  • 2020-12-09 10:40

    In Asp.Net webforms, you cannot escape the fact that framework's entry into your code comes from aspx pages. I agree that your business layer should not touch the asp.net specific components directly but you have to have a model's storage container and session in asp.net is a good area. Thus, one possible approach is to create ISessionManager for purpose of interacting inside your business layer. Then, implement the concrete type by using HttpSessionState ... btw, a good trick is to use HttpContext.Current.Session to implement accessors/getters out of the HttpSessionState. Your next challenge would be how to wire it all together.

    0 讨论(0)
提交回复
热议问题