Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking HttpSessionState in ASP.net for nunit testing

I've see n a lot of discussions surrounding HttpSessionState and asp.net MVC. I'm trying to write tests for an asp.net application and wondering if it's possible to mock the HttpSessionState and if so, how?

I'm currently using Rhino Mocks and Nunit

like image 542
Gilbert Liddell Avatar asked Mar 28 '11 14:03

Gilbert Liddell


3 Answers

look at the HttpSessionStateBase and HttpSessionStateWrapper classes in System.Web.Abstractions. HttpSessionStateBase is the abstract class from which HttpSessionState inherits, and HttpSessionStateWrapper is used to wrap a sealed class in an abstract class, which you can then mock in your tests.

A lot of the System.Web classes are sealed (for example, HttpSessionState), so it's a real pain to test your code when you have methods and classes that interact with them. One pattern I like to use to get around this looks like the following:

public void DoSomething(HttpSessionState state)
{
  // take this HttpSeassionState and create an abstract HttpSessionStateBase
  // instance
  DoSomething(new HttpSessionStateWrapper(state));
}

internal void DoSomething(HttpSessionStateBase state)
{
  // my actual logic for working with the session state
}

The public method is difficult to test, because HttpSessionState is sealed, and you can't mock it. However, the internal method operates on an HttpSessionStateBase instance, which you can mock. Note that I've marked it as internal because I don't want the outside world to be able to access that method. However, I do want my tests to be able to access that, so I'll modify my AssemblyInfo.cs to include something like this:

[assembly: InternalsVisibleTo("Vendor.Utilities.Tests")] 

Finally, my test for this would look something like this:

[Test]
public void Test_DoSomething()
{
  HttpSessionStateBase state = MockRepository.PartialMock<HttpSessionStateBase>();
  state.Expect(s => ...);

  MyClass.DoSomething(state);

  state.VerifyAllExpectations();
}

Hope that helps. Good luck!

like image 94
matt Avatar answered Oct 22 '22 06:10

matt


Gilbert,

Maybe I'm too late for you. I'm using MSpec, but I think the concepts are similar. I needed to mock several components of the HttpContext in the controllers under test.

I started with these following classes to mock up the necessary (for my purposes) components in the HttpContextBase. I overrode only the necessary pieces inside the classes. Your needs will vary as to the mocks you need in the controller. It's fairly easy to add mocks as needed once you understand the pattern.

public class MockHttpContext : HttpContextBase 
{

    private readonly HttpRequestBase _request = new MockHttpRequest();
    private readonly HttpServerUtilityBase _server = new MockHttpServerUtilityBase();
    private HttpSessionStateBase _session = new MockHttpSession();

    public override HttpRequestBase Request
    {
        get { return _request; }
    }
    public override HttpServerUtilityBase Server
    {
        get { return _server; }
    }
    public override HttpSessionStateBase Session
    {
        get { return _session; }
    }
}

public class MockHttpRequest : HttpRequestBase
{
    private Uri _url = new Uri("http://www.mockrequest.moc/Controller/Action");

    public override Uri Url
    {
        get { return _url; }
    }
}

public class MockHttpServerUtilityBase : HttpServerUtilityBase
{
    public override string UrlEncode(string s)
    {
        //return base.UrlEncode(s);     
        return s;       // Not doing anything (this is just a Mock)
    }
}


public class MockHttpSession : HttpSessionStateBase
{
    // Started with sample http://stackoverflow.com/questions/524457/how-do-you-mock-the-session-object-collection-using-moq
    // from http://stackoverflow.com/users/81730/ronnblack

    System.Collections.Generic.Dictionary<string, object> _sessionStorage = new   System.Collections.Generic.Dictionary<string,object>();
    public override object this[string name]
    {
        get { return _sessionStorage[name]; }
        set { _sessionStorage[name] = value; }
    }

    public override void Add(string name, object value)
    {
        _sessionStorage[name] = value;
    }
}

Here is how I setup the Controller Context to use the mocks (MSpec). This is setup for the actual tests on the contoller (the tests derive from this class)

public abstract class BlahBlahControllerContext
{
    protected static BlahBlahController controller;

    Establish context = () =>
    {
        controller = new BlahBlahController();
        controller.ControllerContext = new ControllerContext()
        {
            Controller = controller,
            RequestContext = new RequestContext(new MockHttpContext(), new RouteData()),
        };
    };
}

To further illustrate here is a test (Specification in MSpec world) that uses the mock session:

[Subject("ACCOUNT: Retrieve Password")]
public class retrieve_password_displays_retrieve_password2_page_on_success : BlahBlahControllerContext
{
    static ActionResult result;
    static RetrievePasswordModel model;

    Establish context = () =>
    {
        model = new RetrievePasswordModel()
        {
            UserName = "Mike"
        };
    };

    Because of = () =>
    {
        result = controller.RetrievePassword(model);
    };

    It should_return_a_RedirectToRouteResult = () => 
    { 
        result.is_a_redirect_to_route_and().action_name().ShouldEqual("RetrievePassword2"); 
    };

    It session_should_contain_UN_value = () =>
    {
        controller.HttpContext.Session["UN"].ShouldEqual("Mike");
    };

    It session_should_contain_PQ_value = () =>
    {
        controller.HttpContext.Session["PQ"].ShouldEqual("Question");
    };
}

I realize this doesn't use Rhino Mocks. I hope it illustrates the principles and readers can adopt it to their specific tools and methods.

like image 11
MikeSullivan Avatar answered Oct 22 '22 06:10

MikeSullivan


If you need to instantiate exactly HttpSessionState for legacy code tests, you can leverage FormatterServices mechanism to get uninitialized object. To get it working it is needed to set private _container field though, like in internal constructor

Example:

var state = (HttpSessionState) System.Runtime.Serialization
    .FormatterServices.GetUninitializedObject(typeof(HttpSessionState));

var containerFld = typeof(HttpSessionState).GetField(
    "_container", BindingFlags.Instance | BindingFlags.NonPublic);

var itemCollection = new SessionStateItemCollection();
itemCollection["element"] = 1;

containerFld.SetValue(
    state,
    new HttpSessionStateContainer(
        "1",
        itemCollection,
        new HttpStaticObjectsCollection(),
        900,
        true,
        HttpCookieMode.UseCookies,
        SessionStateMode.InProc,
        false
    )
);
like image 10
RR-Fireball Avatar answered Oct 22 '22 06:10

RR-Fireball