模拟会话在MVC 5中不起作用
我正在我的控制器中的Session中存储值正在测试的Action。 我已经阅读了几篇关于如何模拟会话的文章,我正在尝试实现Milox 在unit testing中设置httpcontext当前会话的答案。 但是当我钻进Locals | this | base | HttpContext
Locals | this | base | HttpContext
当设置Session变量HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;
Locals | this | base | HttpContext
Sessions
仍为null并且测试失败,并带有Null ReferenceexceptionHttpContext.Session["BsAcId"] = vM.BusAcnt.Id;
这是工作生产代码。 vM.BusAcnt.Id
返回一个有效的int
,如果我用int
值替换它,测试仍然失败,因为Session
为null,因此没有值可以存储在其中。
我正在使用MVC5,EF6以及最新版本的xUnit,Moq和Resharper测试运行器。
行动:
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; } }
测试:
[Fact] public void AdminGetBusAcntById() { HttpContext.Current = MockHelpers.FakeHttpContext(); var mockMyDb = MockDbSetup.MockMyDb(); var controller = new BusAcntController(mockMy.Object); var controllerContextMock = new Mock(); controllerContextMock.Setup( x => x.HttpContext.User. IsInRole(It.Is(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的代码似乎有意义,但我无法让它工作。 我错过了什么吗? MVC5有没有破坏这段代码的变化?
解:
实现达林的答案。 我现在有一个会话来编写值(虽然这些值实际上没有被写入,但是为了测试目的不需要)并且测试通过了。
测试:
[Fact] public void AdminGetBusAcntById() { var mockMyDb = MockDbSetup.MockMyDb(); var controller = new BusAcntController(mockMy.Object); var context = new Mock(); var session = new Mock(); 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); }
在你的unit testing中你设置了HttpContext.Current = MockHelpers.FakeHttpContext();
但是ASP.NET MVC根本不使用这个静态属性。 忘掉ASP.NET MVC中的HttpContext.Current
。 它的遗留和unit testing不友好(是的,在您的情况下,您只在unit testing中使用它,但ASP.NET MVC不使用它,这就是您的代码不起作用的原因)。
重点是ASP.NET MVC正在使用HttpContextBase,HttpRequestBase,HttpResponseBase,HttpSessionStateBase等抽象,您可以轻松地在unit testing中进行模拟。
我们来看一个示例控制器:
public class HomeController : Controller { public ActionResult Index() { if ((this.User.IsInRole("Admin"))) { return RedirectToAction("Action"); } this.HttpContext.Session["foo"] = "bar"; return View(); } }
以及如何通过使用Moq模拟所需的抽象来进行相应的unit testing:
// arrange var controller = new HomeController(); var context = new Mock(); var session = new Mock(); 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"); ...
如果您想要输入User.IsInRole("Admin")
条件,您所要做的就是为模拟身份提供适当的角色。
方式,我将使用MOQ应用MOCKing of Sessions如下。
我将在UnitTests项目中创建一个基类。 结构将是
[TestFixture] public class BaseClass { public Mock controllerContext; public Mock contextBase; public BaseClass() { controllerContext = new Mock(); contextBase = new Mock(); controllerContext.Setup(x => x.HttpContext).Returns(contextBase.Object); controllerContext.Setup(cc => cc.HttpContext.Session["UserId"]).Returns(1); } }
请参阅:我在最后一行返回1作为UserId的会话值。 您可以根据要求进行更改。
为了便于参考,我将我的TestClass命名为“ControllerClassTest”。 所以我会像这样用BaseClassinheritanceControllerClassTest
[TestFixture] class ControllerClassTest : BaseClass { }
然后,在我的测试类中,我会像这样在Setup方法中初始化ControllerContext
[SetUp] public void Setup() { controller.ControllerContext = controllerContext.Object; }
不要忘记,我们必须先声明并初始化控制器。
我希望,它可以帮助你