如何在ASP.Net MVC模拟控制器上的请求?
我有一个控制器在C#使用ASP.Net MVC框架
public class HomeController:Controller{ public ActionResult Index() { if (Request.IsAjaxRequest()) { //do some ajaxy stuff } return View("Index"); } }
我有一些嘲讽的技巧,并希望用以下和RhinoMockstesting代码
var mocks = new MockRepository(); var mockedhttpContext = mocks.DynamicMock<HttpContextBase>(); var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>(); SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest); var controller = new HomeController(); controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller); var result = controller.Index() as ViewResult; Assert.AreEqual("About", result.ViewName);
不过,我不断收到这个错误:
exceptionSystem.ArgumentNullException:System.ArgumentNullException:值不能为空。 参数名称:请求在System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest(HttpRequestBase请求)
由于控制器上的Request
对象没有setter。 我试图通过使用下面的答案推荐的代码来正确工作。
这使用Moq而不是RhinoMocks,并在使用Moq我使用以下相同的testing:
var request = new Mock<HttpRequestBase>(); // Not working - IsAjaxRequest() is static extension method and cannot be mocked // request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */); // use this request.SetupGet(x => x.Headers["X-Requested-With"]).Returns("XMLHttpRequest"); var context = new Mock<HttpContextBase>(); context.SetupGet(x => x.Request).Returns(request.Object); var controller = new HomeController(Repository, LoginInfoProvider); controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller); var result = controller.Index() as ViewResult; Assert.AreEqual("About", result.ViewName);
但得到以下错误:
exceptionSystem.ArgumentException:System.ArgumentException:对不可重写成员的无效设置:x => x.Headers [“X-Requested-With”] at Moq.Mock.ThrowIfCantOverride(Expression setup,MethodInfo methodInfo)
再次,似乎我不能设置请求标题。 如何在RhinoMocks或Moq中设置这个值?
使用Moq :
var request = new Mock<HttpRequestBase>(); // Not working - IsAjaxRequest() is static extension method and cannot be mocked // request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */); // use this request.SetupGet(x => x.Headers).Returns( new System.Net.WebHeaderCollection { {"X-Requested-With", "XMLHttpRequest"} }); var context = new Mock<HttpContextBase>(); context.SetupGet(x => x.Request).Returns(request.Object); var controller = new YourController(); controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
更新:
模拟Request.Headers["X-Requested-With"]
或Request["X-Requested-With"]
而不是Request.IsAjaxRequest()
。
这是一个使用RhinoMocks的工作解决scheme。 我已经根据我在http://thegrayzone.co.uk/blog/2010/03/mocking-request-isajaxrequest/上find的Moq解决scheme
public static void MakeAjaxRequest(this Controller controller) { MockRepository mocks = new MockRepository(); // Create mocks var mockedhttpContext = mocks.DynamicMock<HttpContextBase>(); var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>(); // Set headers to pretend it's an Ajax request SetupResult.For(mockedHttpRequest.Headers) .Return(new WebHeaderCollection() { {"X-Requested-With", "XMLHttpRequest"} }); // Tell the mocked context to return the mocked request SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest); mocks.ReplayAll(); // Set controllerContext controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller); }
对于任何使用NSubstitute的人,我都能够修改上面的答案,并做这样的事情…(其中Details是控制器上的Action方法名称)
var fakeRequest = Substitute.For<HttpRequestBase>(); var fakeContext = Substitute.For<HttpContextBase>(); fakeRequest.Headers.Returns(new WebHeaderCollection { {"X-Requested-With", "XMLHttpRequest"}}); fakeContext.Request.Returns(fakeRequest); controller.ControllerContext = new ControllerContext(fakeContext, new RouteData(), controller); var model = new EntityTypeMaintenanceModel(); var result = controller.Details(model) as PartialViewResult; Assert.IsNotNull(result); Assert.AreEqual("EntityType", result.ViewName);
AjaxRequest是一个扩展方法。 所以你可以用下面的方法使用Rhino:
protected HttpContextBase BuildHttpContextStub(bool isAjaxRequest) { var httpRequestBase = MockRepository.GenerateStub<HttpRequestBase>(); if (isAjaxRequest) { httpRequestBase.Stub(r => r["X-Requested-With"]).Return("XMLHttpRequest"); } var httpContextBase = MockRepository.GenerateStub<HttpContextBase>(); httpContextBase.Stub(c => c.Request).Return(httpRequestBase); return httpContextBase; } // Build controller .... controller.ControllerContext = new ControllerContext(BuildHttpContextStub(true), new RouteData(), controller);
你需要嘲笑HttpContextBase并把它放到你的ControllerContext属性中,像这样:
controller.ControllerContext = new ControllerContext(mockedHttpContext, new RouteData(), controller);
在这里你可以看到如何模拟Form集合,你的场景会是类似的: 在ASP.NET MVC中嘲讽HttpRequest
看起来你正在寻找这个,
var requestMock = new Mock<HttpRequestBase>(); requestMock.SetupGet(rq => rq["Age"]).Returns("2001");
控制器中的用法:
public ActionResult Index() { var age = Request["Age"]; //This will return 2001 }
要在unit testing期间使IsAjaxRequest()
返回false,您需要在您的testing方法中设置Request Headers以及请求集合值,如下所示:
_request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "NotAjaxRequest" } }); _request.SetupGet(x=>x["X-Requested-With"]).Returns("NotAjaxRequest");
设置的原因隐藏在下面给出的IsAjaxRequest()的实现中:
public static bool IsAjaxRequest(this HttpRequestBase request)<br/> { if (request == null) { throw new ArgumentNullException("request"); } return ((request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest"))); }
它同时使用请求集合和标题,这就是为什么我们需要为标题和请求集合创build设置。
这将使请求返回false,当它不是一个Ajax请求。 要使其返回true,您可以执行以下操作:
_httpContext.SetupGet(x => x.Request["X-Requested-With"]).Returns("XMLHttpRequest");