【发布时间】:2014-05-10 21:56:32
【问题描述】:
我将值存储在正在测试的控制器 Action 中的 Session 中。我已经阅读了几篇关于如何模拟会话的文章,并且我正在尝试实现 Milox 对Setting the httpcontext current session in unit test 的回答。但是当我深入到Locals | this | base | HttpContext Sessions 仍然为空并且设置会话变量HttpContext.Session["BsAcId"] = vM.BusAcnt.Id; 时测试失败并出现空引用异常时@
这是有效的生产代码。 vM.BusAcnt.Id 返回一个有效的int,如果我用int 值替换它,测试仍然失败,因为Session 为空,因此不能在其中存储任何值。
我正在使用 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);
}
模拟助手:
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<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 的代码似乎有道理,但我无法让它工作。 我错过了什么吗? MVC5 中是否存在破坏此代码的更改?
解决方案:
达林回答的实施。我现在有一个 Session 来写入值(尽管这些值实际上并没有被写入其中,但这不是测试目的所必需的)并且测试通过了。
测试:
[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);
}
【问题讨论】:
标签: c# session mocking asp.net-mvc-5