Come deridere la richiesta sul controller in ASP.Net MVC?


171

Ho un controller in C # usando il framework ASP.Net MVC

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

Ho avuto alcuni consigli sul deridere e speravo di testare il codice con il seguente e 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);

Tuttavia continuo a ricevere questo errore:

Eccezione System.ArgumentNullException: System.ArgumentNullException: il valore non può essere nullo. Nome parametro: richiesta su System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest (richiesta HttpRequestBase)

Poiché l' Requestoggetto sul controller non ha setter. Ho provato a far funzionare correttamente questo test utilizzando il codice consigliato da una risposta di seguito.

Questo ha usato Moq invece di RhinoMocks e, usando Moq, utilizzo lo stesso per lo stesso test:

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

ma ottieni il seguente errore:

Eccezione System.ArgumentException: System.ArgumentException: installazione non valida su un membro non sostituibile: x => x.Headers ["X-Requested-With"] su Moq.Mock.ThrowIfCantOverride (Setup espressione, MethodInfo methodInfo)

Ancora una volta, sembra che non sia possibile impostare l'intestazione della richiesta. Come posso impostare questo valore, in RhinoMocks o Moq?


Sostituisci Request.IsAjaxRequest con Request.IsAjaxRequest ()
eu-ge-ne,

Mock Request.Headers ["X-Requested-With"] o Request ["X-Requested-With"] invece di Request.IsAjaxRequest (). Ho aggiornato la mia domanda
eu-ge-ne,

Risposte:


212

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

AGGIORNAMENTO:

Deridere Request.Headers["X-Requested-With"]o Request["X-Requested-With"]invece di Request.IsAjaxRequest().


1
Ricevo il messaggio "L'argomento Type per il metodo 'ISetupGetter <T, TProperty> Moq.Mock <T> .SetupGet <Tpropert> .... non può essere dedotto da uage. Prova a specificare esplicitamente gli argomenti del tipo. Che tipo devo impostare 'var request =' to if to if to get to how to get?
Nissan

Ho appena aggiornato la mia risposta - non Request.IsAjaxRequest ma Request.IsAjaxRequest (). Aggiorna anche la tua domanda
eu-ge-ne,

Genera ancora: Eccezione System.ArgumentException: System.ArgumentException: installazione non valida su un membro non sostituibile: x => x.IsAjaxRequest () su Moq.Mock.ThrowIfCantOverride (installazione di espressioni, MethodInfo methodInfo)
Nissan

Il problema è che IsAjaxRequest () è un metodo di estensione statica e non può essere deriso: ho aggiornato la mia risposta.
eu-ge-ne,

dovrebbe essere context.SetupGet (x => x.Request) .Returns (request.Object); il tuo codice sopra manca la 's' su Return ancora Si traduce anche in Eccezione System.ArgumentException: System.ArgumentException: installazione non valida su un membro non sostituibile: x => x.Headers ["X-Requested-With"] su Moq .Mock.ThrowIfCantOverride (Expression setup, MethodInfo methodInfo) messaggio di errore
Nissan

17

Per chiunque usi NSubstitute sono stato in grado di modificare le risposte sopra e fare qualcosa del genere ... (dove Dettagli è il nome del metodo di azione sul controller)

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

13

Ecco una soluzione funzionante con RhinoMocks. L'ho basato su una soluzione Moq che ho trovato su 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);
}

5

AjaxRequest è un metodo di estensione. Quindi puoi farlo nel modo seguente 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);

4

Sembra che tu stia cercando questo,

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

Utilizzo nel controller:

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

3

Devi deridere HttpContextBase e inserirlo nella proprietà ControllerContext, in questo modo:

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

e cosa dovrebbe essere deriso DerbyHttpContext? Per l'oggetto RequestContext richiesto è necessario un oggetto HttpContextBase () nel costruttore e HttpContextBase () non ha un costruttore che accetta parametri zero.
Nissan,

Ho provato: var mocks = new MockRepository (); var mockedhttpContext = mocks.DynamicMock <HttpContextBase> (); var mockedHttpRequest = mocks.DynamicMock <HttpRequestBase> (); SetupResult.For (mockedhttpContext.Request) .Return (mockedHttpRequest); var controller = new HomeController (Repository, LoginInfoProvider); controller.ControllerContext = new mockedhttpContext, new RouteData (), controller); var result = controller.Index () come ViewResult; Tuttavia viene comunque generata la stessa eccezione.
Nissan,

Il tuo link non funziona, ma il seguente sembra funzionare _request.Setup (o => o.Form) .Returns (new NameValueCollection ());
Vdex,

2

Per rendere IsAjaxRequest()restituito falso durante il test unità è necessario impostare le intestazioni richiesta e il valore raccolta richieste sia nel metodo di test indicato di seguito:

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

Il motivo della configurazione di entrambi è nascosto nell'implementazione di IsAjaxRequest () che viene fornito di seguito:

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

Utilizza sia la raccolta richieste che l'intestazione, per questo motivo dobbiamo creare un'impostazione sia per l'intestazione sia per la raccolta richieste.

questo renderà falsa la richiesta quando non è una richiesta Ajax. per farlo tornare vero puoi fare quanto segue:

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

0

Ho trovato un altro modo per aggiungere un oggetto HttpRequestMessage alla tua richiesta durante l'API Web come segue

[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
}

0

(Un po 'tardi alla festa ma sono andato per un percorso diverso, quindi ho pensato di condividere)

Per cercare un codice puro / un modo beffardo di testarlo senza creare simulazioni per le classi Http, ho implementato un IControllerHelper che ha un metodo Initialise che accetta la richiesta come parametro e quindi espone le proprietà che voglio ad esempio:

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

Quindi nel mio controller chiamo inizializzazione all'inizio del metodo:

        _controllerHelper.Initialise(Request);

E poi il mio codice dipende solo da dipendenze beffardo.

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

Per i test funzionali ho appena sostituito iControllerHelper nella composizione per un sostituto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.