嘲讽会话不MVC 5个工作 [英] Mocking Session not working in MVC 5

查看:89
本文介绍了嘲讽会话不MVC 5个工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我存储在我的控制器行动在Session值进行测试。我读过关于如何嘲笑会话几篇文章,我试图实现Milox的回答<一个href=\"http://stackoverflow.com/questions/9624242/setting-the-httpcontext-current-session-in-unit-test/10126711#10126711\">Setting在单元测试 HttpContext的当前会话。但是,当我钻到当地人|这|基地| HttpContext的 会话仍是null,设置会话变量时测试失败,一个空引用异常 HttpContext.Session [ BsAcId] = vM.BusAcnt.Id;

这是工作生产code。 vM.BusAcnt.Id 返回一个有效的 INT 如果我用 INT <替代它/ code>值测试仍然失败,因为会话为空,因此没有值可以存储在其中。

我使用MVC5,EF6和的xUnit,起订量的最新版本和ReSharper的测试运行。

动作:

 公共ActionResult的详细信息(INT ID)
{
  VAR VM =新BusAcntVm();
  vM.BusAcnt = _db.BusAcnts.FirstOrDefault(BA =&GT; bA.Id == ID);  如果((User.IsInRole(管理)))返回RedirectToAction(行动);  HttpContext.Session [BsAcId] = vM.BusAcnt.Id;  返回查看(VM);
}

MockHelpers:

 公共静态类MockHelpers
  {
    公共静态的HttpContext FakeHttpContext()
    {
      变种的htt prequest =新的Htt prequest(,HTTP://本地主机/,);
      VAR的StringWriter =新的StringWriter();
      VAR HTT presponce =新的Htt presponse(StringWriter的);
      VAR的HttpContext =新的HttpContext(HTT prequest,HTT presponce);      VAR sessionContainer =新HttpSessionStateContainer(ID,新SessionStateItemCollection()
                                              新HttpStaticObjectsCollection(),10,真实,
                                              HttpCookieMode.AutoDetect,
                                              SessionStateMode.InProc,FALSE);      httpContext.Items [AspSession] = typeof运算(HttpSessionState).GetConstructor(
                                  BindingFlags.NonPublic可| BindingFlags.Instance,
                                  空,CallingConventions.Standard,
                                  新的[] {typeof运算(HttpSessionStateContainer)},
                                  空值)
                          .Invoke(新的对象[] {sessionContainer});      返回HttpContext的;
    }
  }

测试:

  [事实]
公共无效AdminGetBusAcntById()
{
  HttpContext.Current = MockHelpers.FakeHttpContext();
  变种mockMyDb = MockDbSetup.MockMyDb();
  VAR控制器=新BusAcntController(mockMy.Object);
  VAR controllerContextMock =新的模拟&LT; ControllerContext&GT;();
  controllerContextMock.Setup(X =&GT; x.HttpContext.User。
    IsInRole(It.Is&LT;串将(S = GT; s.Equals(管理))))返回(真)。
  controller.ControllerContext = controllerContextMock.Object;  变种的ViewResult = controller.Details(1)的ViewResult;
  VAR模型= viewResult.Model为BusAcntVm;  Assert.NotNull(模型);
  Assert.Equal(「本公司1,model.CmpnyName);
}

Milox的code似乎很有道理,但我无法得到它的工作。
我错过了什么?是否有MVC5打破这个code的改变?

解决方案:

达林的回答的实现。我现在有一个会议,以编写针对值(虽然值实际上并不被写入了进去,但是这并不需要进行测试的目的),并测试通过。

测试:

  [事实]
公共无效AdminGetBusAcntById()
{
  变种mockMyDb = MockDbSetup.MockMyDb();
  VAR控制器=新BusAcntController(mockMy.Object);
  VAR语境=新的模拟&LT; HttpContextBase&GT;();
  VAR会话=新的模拟&LT; HttpSessionStateBase&GT;();
  VAR用户=新的GenericPrincipal(新GenericIdentity(fakeUser此),新的[] {管理员});
  context.Setup(X =&GT; x.User).Returns(用户);
  context.Setup(X =&GT; x.Session).Returns(session.Object);
  VAR的RequestContext =新的RequestContext(context.Object,新的RouteData());
  controller.ControllerContext =新的ControllerContext(RequestContext的控制器);
  变种的ViewResult = controller.Details(1)的ViewResult;
  VAR模型= viewResult.Model为BusAcntVm;  Assert.NotNull(模型);
  Assert.Equal(「本公司1,model.CmpnyName);
}


解决方案

在你的单元测试已设置 HttpContext.Current = MockHelpers.FakeHttpContext(); ,但ASP。 NET MVC根本不使用这种静态属性。忘掉 HttpContext.Current 在ASP.NET MVC。这是传统和单元测试不友好(是的,你的情况,你正在使用它里面只有你的单元测试,但ASP.NET MVC不使用它,这就是为什么你的code不工作的原因)。

整点是,ASP.NET MVC正在与抽象,例如HttpContextBase,Htt的prequestBase,Htt的presponseBase,HttpSessionStateBase,......你可以很容易地在你的单元测试嘲笑。

让我们看一个例子控制器:

 公共类HomeController的:控制器
{
    公众的ActionResult指数()
    {
        如果((this.User.IsInRole(管理)))
        {
            返回RedirectToAction(行动);
        }        this.HttpContext.Session [富] =酒吧;        返回查看();
    }
}

和如何相应的单元测试可能看起来像使用嘲讽起订量要求的抽象:

  //安排
VAR控制器=新的HomeController();
VAR语境=新的模拟&LT; HttpContextBase&GT;();
VAR会话=新的模拟&LT; HttpSessionStateBase&GT;();
VAR用户=新的GenericPrincipal(新GenericIdentity(约翰),新的[] {贡献者});
context.Setup(X =&GT; x.User).Returns(用户);
context.Setup(X =&GT; x.Session).Returns(session.Object);
VAR的RequestContext =新的RequestContext(context.Object,新的RouteData());
controller.ControllerContext =新的ControllerContext(RequestContext的控制器);//行为
VAR实际= controller.Index();//断言
session.VerifySet(X =&GT; X [富] =巴);
...

如果你想进入 User.IsInRole(管理)条件下,所有你需要做的就是提供给嘲笑身份应有的作用。

I'm storing values in the Session in my controller Action being tested. I've read several articles on how to mock a session and I'm trying to implement Milox's answer to Setting the httpcontext current session in unit test. But when I drill into Locals | this | base | HttpContext Sessions is still null and the test fails with a Null Reference exception when setting the Session variable HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;

This is working production code. vM.BusAcnt.Id returns a valid int and if I substitute it with an int value the test still fails because the Session is null and therefore no value can be stored in it.

I'm using MVC5, EF6, and the latest versions of xUnit, Moq and the Resharper test runner.

Action:

public ActionResult Details(int id)
{
  var vM = new BusAcntVm();
  vM.BusAcnt = _db.BusAcnts.FirstOrDefault(bA => bA.Id == id);

  if ((User.IsInRole("Admin"))) return RedirectToAction("Action");

  HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;

  return View(vM);
}

MockHelpers:

  public static class MockHelpers
  {
    public static HttpContext FakeHttpContext()
    {
      var httpRequest = new HttpRequest("", "http://localhost/", "");
      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;
    }
  }

Test:

[Fact] 
public void AdminGetBusAcntById()
{
  HttpContext.Current = MockHelpers.FakeHttpContext();
  var mockMyDb = MockDbSetup.MockMyDb();
  var controller = new BusAcntController(mockMy.Object);
  var controllerContextMock = new Mock<ControllerContext>();
  controllerContextMock.Setup( x => x.HttpContext.User.
    IsInRole(It.Is<string>(s => s.Equals("Admin")))).Returns(true);
  controller.ControllerContext = controllerContextMock.Object;

  var viewResult = controller.Details(1) as ViewResult;
  var model = viewResult.Model as BusAcntVm;

  Assert.NotNull(model);
  Assert.Equal("Company 1", model.CmpnyName);
}

Milox's code seems to make sense but I can't get it to work. Have I missed something? Is there a change in MVC5 that breaks this code?

SOLUTION:

Implementation of Darin's answer. I now have a Session to write the values against (though the values don't actually get written into it, but that's not needed for the purpose of testing) and the test passes.

Test:

[Fact] 
public void AdminGetBusAcntById()
{
  var mockMyDb = MockDbSetup.MockMyDb();
  var controller = new BusAcntController(mockMy.Object);
  var context = new Mock<HttpContextBase>();
  var session = new Mock<HttpSessionStateBase>();
  var user = new GenericPrincipal(new GenericIdentity("fakeUser"), new[] { "Admin" });
  context.Setup(x => x.User).Returns(user);
  context.Setup(x => x.Session).Returns(session.Object);
  var requestContext = new RequestContext(context.Object, new RouteData());
  controller.ControllerContext = new ControllerContext(requestContext, controller);


  var viewResult = controller.Details(1) as ViewResult;
  var model = viewResult.Model as BusAcntVm;

  Assert.NotNull(model);
  Assert.Equal("Company 1", model.CmpnyName);
}

解决方案

In your unit test you have set HttpContext.Current = MockHelpers.FakeHttpContext(); but ASP.NET MVC doesn't use this static property at all. Forget about HttpContext.Current in ASP.NET MVC. It's legacy and unit testing unfriendly (yes, in your case you are using it only inside your unit test, but ASP.NET MVC doesn't use it and is the reason why your code doesn't work).

The whole point is that ASP.NET MVC is working with abstractions such as HttpContextBase, HttpRequestBase, HttpResponseBase, HttpSessionStateBase, ... that you could easily mock in your unit test.

Let's take an example controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        if ((this.User.IsInRole("Admin")))
        {
            return RedirectToAction("Action");
        }

        this.HttpContext.Session["foo"] = "bar";

        return View();
    }
}

and how a corresponding unit test might look like by mocking the required abstractions using Moq:

// arrange
var controller = new HomeController();
var context = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
var user = new GenericPrincipal(new GenericIdentity("john"), new[] { "Contributor" });
context.Setup(x => x.User).Returns(user);            
context.Setup(x => x.Session).Returns(session.Object);
var requestContext = new RequestContext(context.Object, new RouteData());
controller.ControllerContext = new ControllerContext(requestContext, controller);

// act
var actual = controller.Index();

// assert
session.VerifySet(x => x["foo"] = "bar");
...

And if you wanted to enter the User.IsInRole("Admin") condition, all you have to do is provide the proper role to the mocked identity.

这篇关于嘲讽会话不MVC 5个工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆