如何在 ASP.Net MVC 中模拟控制器请求?

我在 C # 中有一个控制器,它使用的是 ASP.NET MVC Framework

public class HomeController:Controller{
public ActionResult Index()
{
if (Request.IsAjaxRequest())
{
//do some ajaxy stuff
}
return View("Index");
}
}

我得到了一些关于嘲笑的技巧,并希望用以下代码和 RhinoMocks 测试代码

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);

然而,我不断得到这个错误:

例外 System.ArgumentNullException: System.ArgumentNullException: 值 参数名称: 请求 IsAjaxRequest (HttpRequestBase) 要求)

因为控制器上的 Request对象没有 setter。我试图通过使用下面答案中推荐的代码来使这个测试正常工作。

这里使用的是 Moq 而不是 RhinoMocks,在使用 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["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);

但得到以下错误:

例外: System.ArgumentException: 无效 在不可重写的成员上设置: x = > x. Header [“ X-Request-With”] at Moq. Mock. ThrowIfCantOverride (Expression MethodInfo methodInfo)

同样,看起来我不能设置请求头。 如何在 RhinoMocks 或 Moq 中设置这个值?

97245 次浏览

您需要模拟 HttpContextBase 并将其放入 ControllerContext 属性中,如下所示:

controller.ControllerContext =
new ControllerContext(mockedHttpContext, new RouteData(), controller);

使用 莫克:

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()

Is 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);

下面是一个使用 RhinoMocks 的工作解决方案,它是基于我在 http://thegrayzone.co.uk/blog/2010/03/mocking-request-isajaxrequest/上找到的一个 Moq 解决方案

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);
}

为了让 IsAjaxRequest()在单元测试期间返回 false,你需要在你的测试方法中设置请求头和请求收集值,如下所示:

_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")));
}

它同时使用请求集合和头部,这就是为什么我们需要为头部和请求集合创建设置。

这将使返回的请求在不是 ajax 请求时为 false。为了让它返回真实,你可以做以下事情:

_httpContext.SetupGet(x => x.Request["X-Requested-With"]).Returns("XMLHttpRequest");

对于任何使用 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);

看来你在找这个,

 var requestMock = new Mock<HttpRequestBase>();
requestMock.SetupGet(rq => rq["Age"]).Returns("2001");

控制器的用法:

 public ActionResult Index()
{
var age = Request["Age"]; //This will return 2001
}

我找到了在 Web API 期间将 HttpRequestMessage 对象添加到请求中的其他方法,如下所示

[Test]
public void TestMethod()
{
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("TestHeader", "TestHeader");
controllerContext.Request = request;
_controller.ControllerContext = controllerContext;


var result = _controller.YourAPIMethod();
//Your assertion
}

(派对有点晚了,但我选择了另一条路线,所以想和大家分享一下)

为了在不为 Http 类创建模拟的情况下进行纯代码/模拟方式的测试,我实现了一个 IControllerHelper,它有一个 Initialize 方法,将 Request 作为一个参数,然后公开属性,我想例如:

    public interface IControllerHelper
{
void Initialise(HttpRequest request);
string HostAddress { get; }
}


public class ControllerHelper : IControllerHelper
{
private HttpRequest _request;
        

public void Initialise(HttpRequest request)
{
_request = request;
}


public string HostAddress =>  _request.GetUri().GetLeftPart(UriPartial.Authority);
}

然后在我的控制器中,我在方法的开头调用 initialize:

        _controllerHelper.Initialise(Request);

然后我的代码只依赖于可仿依赖性。

        return Created(new Uri($"{_controllerHelper.HostName}/api/MyEndpoint/{result.id}"), result);

对于功能测试,我只是重写组合中的 iControllerHelper 来替换它。

在当前.NET (v 5)中:

var controller = new SomeController(); // SomeController that inherits Microsoft.AspNetCore.Mvc.ControllerBase
var httpContext = new DefaultHttpContext(); // DefaultHttpContext class is part of Microsoft.AspNetCore.Http namespace
httpContext.Request.Headers.Add("origin", "0.0.0.1"); // Add your custom headers to request
controller.ControllerContext.HttpContext = httpContext;