单元测试中的会话为null

时间:2018-04-30 08:46:29

标签: c# asp.net-mvc unit-testing session httpcontext

我正在尝试为相当基本的控制器操作编写一些单元测试,其中我设置了Session [" User"]。当我运行测试时,它在此时失败并带有空引用异常。不完全确定如何绕过这个。

控制器:

public ActionResult Index(int id)
        {
            Session["User"] = Id;

            if (Id != 0)
            {
                return RedirectToAction("Home", "Home", new { Id});
            }

            return RedirectToAction("Register", "Home", new { Id});
        }

测试:

[TestMethod]
        public void NewUser_ShouldGoToRegister()
        {
            var controller = new HomeController();

            HttpContext.Current = FakeHttpContext();

            HttpContext.Current.Session.Add("User", "0");

            var result = controller.Index(0) as ViewResult;

            Assert.AreEqual("Register", result.ViewName);
        }


        public HttpContext FakeHttpContext()
        {
            var httpRequest = new HttpRequest("", "http://localhost/", "");

            var httpResponce = new HttpResponse(new StringWriter());

            var httpContext = new HttpContext(httpRequest, httpResponce);
            var sessionContainer =
                new HttpSessionStateContainer("id",
                                               new SessionStateItemCollection(),
                                               new HttpStaticObjectsCollection(),
                                               10,
                                               true,
                                               HttpCookieMode.AutoDetect,
                                               SessionStateMode.InProc,
                                               false);
            httpContext.Items["AspSession"] =
                typeof(HttpSessionState)
                .GetConstructor(
                                    BindingFlags.NonPublic | BindingFlags.Instance,
                                    null,
                                    CallingConventions.Standard,
                                    new[] { typeof(HttpSessionStateContainer) },
                                    null)
                .Invoke(new object[] { sessionContainer });


            HttpContext.Current = httpContext;
        }

2 个答案:

答案 0 :(得分:1)

您可以使用某些模拟工具(如MOQ进行模拟)来创建虚假HttpContext,您可以尝试使用以下代码。

[TestMethod]
public void NewUser_ShouldGoToRegister()
{
    var controller = new HomeController();
    HttpContext.Current = FakeHttpContext();
    HttpContext.Current.Session.Add("User", 1);
    var result = controller.Index(0) as ViewResult;
    Assert.AreEqual("Register", result.ViewName);
}

您的FakeHttpContext方法应如下所示。

        //Creating a Fake HTTP Context           
        // This is used for testing methods using Session Variables.
        private HttpContext FakeHttpContext()
        {
            var httpRequest = new HttpRequest("", "http://somethig.com/", "");
            var stringWriter = new StringWriter();
            var httpResponce = new HttpResponse(stringWriter);
            var httpContext = new HttpContext(httpRequest, httpResponce);

            var sessionContainer = 
                new HttpSessionStateContainer("id", new SessionStateItemCollection(),
                                                    new HttpStaticObjectsCollection()
                                                    , 10,
                                                    true,
                                                    HttpCookieMode.AutoDetect,
                                                    SessionStateMode.InProc, false);

            httpContext.Items["AspSession"] = 
                typeof(HttpSessionState).GetConstructor(
                                        BindingFlags.NonPublic | BindingFlags.Instance,
                                        null, CallingConventions.Standard,
                                        new[] { typeof(HttpSessionStateContainer) },
                                        null)
                                .Invoke(new object[] { sessionContainer });

            return httpContext;
        }

答案 1 :(得分:0)

如果你有这方面的困难,这个想法可能会帮助你过度思考它。有时,创建Session的另一层访问权限是个好主意。

简化的方法是永远不要直接访问" Session",而是将其包含在这样的方法中:

protected virtual string ReadFromSession(string key)
{
    return Session[key];
}

protected virtual void StoreInSession(string key, string value)
{
    Session[key] = value;
}

在Controller中的任何地方,您只需使用这些方法,而不是直接访问Session。很容易。

现在的诀窍是使用单元测试,您可以覆盖您测试的控制器:

public class MyControllerForTesting : MyController
{
    private readonly IDictionary session;

    public MyControllerForTesting(IDictionary session) : base()
    {
        this.session = session;
    }

    protected override string ReadFromSession(string key)
    {
        return this.session[key];
    }

    protected override void StoreInSession(string key, string value)
    {
        this.session[key] = value;
    }
}

现在这可能不会立即起作用,因为我没有真正测试它,但你得到了想法。您的单元测试工作,如果值设置正确等,您甚至可以检查字典。