¿Cómo burlarse de la solicitud del controlador en ASP.Net MVC?

171

Tengo un controlador en C # usando el marco ASP.Net MVC

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

Recibí algunos consejos sobre burlas y esperaba probar el código con lo siguiente y 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);

Sin embargo, sigo recibiendo este error:

Excepción System.ArgumentNullException: System.ArgumentNullException: el valor no puede ser nulo. Nombre del parámetro: solicitud en System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest (solicitud HttpRequestBase)

Dado que el Requestobjeto en el controlador no tiene setter. Traté de hacer que esta prueba funcionara correctamente usando el código recomendado de una respuesta a continuación.

Esto usó Moq en lugar de RhinoMocks, y al usar Moq utilizo lo siguiente para la misma prueba:

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

pero recibe el siguiente error:

Excepción System.ArgumentException: System.ArgumentException: Configuración no válida en un miembro no reemplazable: x => x.Headers ["X-Requested-With"] en Moq.Mock.ThrowIfCantOverride (Configuración de expresión, MethodInfo methodInfo)

De nuevo, parece que no puedo configurar el encabezado de la solicitud. ¿Cómo configuro este valor, en RhinoMocks o Moq?

Nissan
fuente
Reemplazar Request.IsAjaxRequest con Request.IsAjaxRequest ()
eu-ge-ne
Simulacro Request.Headers ["X-Requested-With"] o Request ["X-Requested-With"] en lugar de Request.IsAjaxRequest (). He actualizado mi pregunta
eu-ge-ne
prueba esto
danfromisrael

Respuestas:

212

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

ACTUALIZADO:

Simulacro Request.Headers["X-Requested-With"]o en Request["X-Requested-With"]lugar de Request.IsAjaxRequest().

eu-ge-ne
fuente
1
Me sale el mensaje "El argumento Tipo para el método 'ISetupGetter <T, TProperty> Moq.Mock <T> .SetupGet <Tpropert> .... no se puede inferir desde uage. Intente especificar los argumentos de tipo explícitamente. ¿Qué tipo configuro? 'var request =' a pesar de conseguir que esto funcione?
Nissan
Acabo de actualizar mi respuesta, no Request.IsAjaxRequest sino Request.IsAjaxRequest (). Actualice su pregunta también
eu-ge-ne
Todavía genera: Excepción System.ArgumentException: System.ArgumentException: Configuración no válida en un miembro no reemplazable: x => x.IsAjaxRequest () en Moq.Mock.ThrowIfCantOverride (Configuración de expresión, MethodInfo methodInfo)
Nissan
El problema es que IsAjaxRequest () es un método de extensión estático y no se puede burlar. He actualizado mi respuesta.
eu-ge-ne
debe ser context.SetupGet (x => x.Request) .Returns (request.Object); a su código anterior le falta la 's' en Devolver todavía También da como resultado Exception System.ArgumentException: System.ArgumentException: Configuración no válida en un miembro no reemplazable: x => x.Headers ["X-Requested-With"] en Moq .Mock.ThrowIfCantOverride (configuración de expresión, MethodInfo methodInfo) mensaje de error
Nissan
17

Para cualquiera que use NSubstitute, pude modificar las respuestas anteriores y hacer algo como esto ... (donde Detalles es el nombre del método de Acción en el controlador)

 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);
sjmarsh
fuente
13

Aquí hay una solución de trabajo con RhinoMocks. Lo he basado en una solución Moq que encontré en http://thegrayzone.co.uk/blog/2010/03/mocking-request-isajaxrequest/

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);
}
Phil Hale
fuente
5

Es AjaxRequest es un método de extensión. Entonces puede hacerlo de la siguiente manera usando 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);
Jeroen Bernsen
fuente
4

Parece que estás buscando esto,

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

Uso en el controlador:

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

Debe burlarse de HttpContextBase y ponerlo en su propiedad ControllerContext, así:

controller.ControllerContext = 
new ControllerContext(mockedHttpContext, new RouteData(), controller);
Michał Chaniewski
fuente
y ¿qué se burlaría de HttpContext? El objeto RequestContext que necesita necesita un objeto HttpContextBase () en el constructor, y HttpContextBase () no tiene un constructor que acepte cero parámetros.
Nissan
Intenté: var mocks = new MockRepository (); var mockedhttpContext = mocks.DynamicMock <HttpContextBase> (); var mockedHttpRequest = mocks.DynamicMock <HttpRequestBase> (); SetupResult.For (mockedhttpContext.Request) .Return (mockedHttpRequest); controlador var = nuevo HomeController (Repository, LoginInfoProvider); controller.ControllerContext = new mockedhttpContext, new RouteData (), controller); resultado var = controller.Index () como ViewResult; Sin embargo, todavía se produce la misma excepción.
Nissan
Su enlace no funciona, pero lo siguiente parece funcionar _request.Setup (o => o.Form) .Returns (new NameValueCollection ());
Vdex
2

Para hacer IsAjaxRequest()que devuelva falso durante la prueba de la Unidad, debe configurar los Encabezados de solicitud, así como el valor de la solicitud de recopilación, tanto en su método de prueba como se indica a continuación:

_request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "NotAjaxRequest" } });
_request.SetupGet(x=>x["X-Requested-With"]).Returns("NotAjaxRequest");

La razón para configurar ambos está oculta en la implementación de IsAjaxRequest () que se detalla a continuación:

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

Utiliza tanto la recopilación de solicitudes como el encabezado, es por eso que necesitamos crear una configuración para el encabezado y la recopilación de solicitudes.

esto hará que la solicitud devuelva falso cuando no sea una solicitud ajax. para que sea verdadero, puede hacer lo siguiente:

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

Encontré otra forma de agregar un objeto HttpRequestMessage en su solicitud durante la API web de la siguiente manera

[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
}
Niraj Trivedi
fuente
0

(Un poco tarde para la fiesta pero fui por una ruta diferente, así que pensé en compartir)

Para buscar un código puro / una forma burlona de probar esto sin crear simulacros para las clases Http, implementé un IControllerHelper que tiene un método Initialise que toma la Solicitud como parámetro y luego expone las propiedades que quiero, por ejemplo:

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

Luego, en mi controlador, llamo initialise al comienzo del método:

        _controllerHelper.Initialise(Request);

Y luego mi código solo depende de dependencias burlables.

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

Para las pruebas funcionales, simplemente anulo el iControllerHelper en la composición para un sustituto.

Stu
fuente