如何在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");