在ASP.net中模拟HttpSessionState进行nunit测试

时间:2011-03-28 14:34:37

标签: asp.net nunit rhino-mocks

我看到很多关于HttpSessionState和asp.net MVC的讨论。 我正在尝试为asp.net应用程序编写测试,并想知道是否可以模拟HttpSessionState,如果是这样,怎么样?

我目前正在使用Rhino Mocks和Nunit

4 个答案:

答案 0 :(得分:11)

吉尔伯特,

也许我来不及你。我正在使用MSpec,但我认为概念是相似的。我需要在被测控制器中模拟HttpContext的几个组件。

我开始使用以下这些类来模拟HttpContextBase中必要的(为了我的目的)组件。我只覆盖了课程中必要的部分。您对控制器中所需的模拟的需求会有所不同。一旦理解了模式,就可以根据需要添加模拟。

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;
    }
}

以下是我如何设置Controller Context以使用模拟(MSpec)。这是为控制器上的实际测试设置的(测试来自这个类)

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()),
        };
    };
}

这里进一步说明是使用模拟会话的测试(MSpec世界中的规范):

[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");
    };
}

我意识到这不使用Rhino Mocks。我希望它能说明这些原则,读者可以将它用于他们的特定工具和方法。

答案 1 :(得分:1)

查看HttpSessionStateBase中的HttpSessionStateWrapperSystem.Web.Abstractions类。 HttpSessionStateBaseHttpSessionState继承的抽象类,HttpSessionStateWrapper用于将抽象类中的密封类包装起来,然后可以在测试中进行模拟。

许多System.Web类都是密封的(例如,HttpSessionState),因此当您拥有与它们交互的方法和类时,测试代码真的很痛苦。我喜欢使用的一种模式如下所示:

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
}

公共方法很难测试,因为HttpSessionState已被密封,您无法模拟它。但是,内部方法在HttpSessionStateBase实例上运行,您可以模拟该实例。请注意,我已将其标记为内部,因为我不希望外部世界能够访问该方法。但是,我希望我的测试能够访问它,所以我将修改我的AssemblyInfo.cs以包含这样的内容:

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

最后,我对此的测试看起来像这样:

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

  MyClass.DoSomething(state);

  state.VerifyAllExpectations();
}

希望有所帮助。祝你好运!

答案 2 :(得分:0)

这是我根据其他人的贡献而制作的......

公共类MockWebContext     {

    public Mock<RequestContext> RoutingRequestContext { get; private set; }
    public Mock<HttpContextBase> Http { get; private set; }
    public Mock<HttpServerUtilityBase> Server { get; private set; }
    public Mock<HttpResponseBase> Response { get; private set; }
    public Mock<HttpRequestBase> Request { get; private set; }
    public Mock<HttpSessionStateBase> Session { get; private set; }
    public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
    public HttpCookieCollection Cookies { get; private set; }

    private IDictionary items;

    public MockWebContext()
    {
        RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
        ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
        Http = new Mock<HttpContextBase>(MockBehavior.Loose);
        Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
        Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
        Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
        Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
        Cookies = new HttpCookieCollection();

        items = new Dictionary<string, object>();

        RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(Http.Object);
        ActionExecuting.SetupGet(c => c.HttpContext).Returns(Http.Object);
        Http.SetupGet(c => c.Request).Returns(Request.Object);
        Http.SetupGet(c => c.Response).Returns(Response.Object);
        Http.SetupGet(c => c.Server).Returns(Server.Object);
        Http.SetupGet(c => c.Session).Returns(Session.Object);
        Http.SetupGet(c => c.Items).Returns(items);
        Request.Setup(c => c.Cookies).Returns(Cookies);
        Request.Setup(c => c.RequestContext).Returns(RoutingRequestContext.Object);
        Response.Setup(c => c.Cookies).Returns(Cookies);
        Session.Setup(c => 
            c.Add(It.IsAny<string>(), It.IsAny<object>())                
            ).Callback((string key, object value)=> items.Add(key, value));

        Session.Setup(c =>
          c.Remove(It.IsAny<string>())
          ).Callback((string key) => items.Remove(key));


        Session.Setup(c =>
          c.Clear()
          ).Callback(() => items.Clear());

        Session.Setup(c =>
        c[It.IsAny<string>()]
        ).Returns((string key)=> items[key]);


    }

}

答案 3 :(得分:-2)