Comment se moquer de la demande sur le contrôleur dans ASP.Net MVC?

171

J'ai un contrôleur en C # utilisant le framework ASP.Net MVC

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

J'ai eu quelques conseils sur la moquerie et j'espérais tester le code avec ce qui suit et 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);

Cependant, je continue à recevoir cette erreur:

Exception System.ArgumentNullException: System.ArgumentNullException: la valeur ne peut pas être nulle. Nom du paramètre: requête à System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest (requête HttpRequestBase)

Puisque l' Requestobjet sur le contrôleur n'a pas de setter. J'ai essayé de faire fonctionner correctement ce test en utilisant le code recommandé à partir d'une réponse ci-dessous.

Cela a utilisé Moq au lieu de RhinoMocks, et en utilisant Moq, j'utilise ce qui suit pour le même 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);

mais obtenez l'erreur suivante:

Exception System.ArgumentException: System.ArgumentException: configuration non valide sur un membre non remplaçable: x => x.Headers ["X-Requested-With"] à Moq.Mock.ThrowIfCantOverride (configuration de l'expression, MethodInfo methodInfo)

Encore une fois, il semble que je ne puisse pas définir l'en-tête de la demande. Comment définir cette valeur dans RhinoMocks ou Moq?

Nissan
la source
Remplacez Request.IsAjaxRequest par Request.IsAjaxRequest ()
eu-ge-ne
Mock Request.Headers ["X-Requested-With"] ou Request ["X-Requested-With"] au lieu de Request.IsAjaxRequest (). J'ai mis à jour ma question
eu-ge-ne
essayez ceci
danfromisrael

Réponses:

212

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

ACTUALISÉ:

Mock Request.Headers["X-Requested-With"]ou Request["X-Requested-With"]au lieu de Request.IsAjaxRequest().

Eugène
la source
1
Je reçois le message "L'argument Type pour la méthode 'ISetupGetter <T, TProperty> Moq.Mock <T> .SetupGet <Tpropert> .... ne peut pas être déduit de uage. Essayez de spécifier les arguments de type explicitement. Quel type dois-je définir 'var request =' à bien pour que cela fonctionne?
Nissan
Je viens de mettre à jour ma réponse - pas Request.IsAjaxRequest mais Request.IsAjaxRequest (). Mettez également à jour votre question
eu-ge-ne
Génère toujours: Exception System.ArgumentException: System.ArgumentException: Configuration non valide sur un membre non remplaçable: x => x.IsAjaxRequest () à Moq.Mock.ThrowIfCantOverride (Configuration de l'expression, MethodInfo methodInfo)
Nissan
Le problème est que IsAjaxRequest () est une méthode d'extension statique et ne peut pas être moquée - j'ai mis à jour ma réponse.
eu-ge-ne
doit être context.SetupGet (x => x.Request) .Returns (request.Object); votre code ci-dessus manque toujours le 's' au retour entraîne toujours une exception System.ArgumentException: System.ArgumentException: configuration non valide sur un membre non remplaçable: x => x.Headers ["X-Requested-With"] chez Moq Message d'erreur .Mock.ThrowIfCantOverride (Configuration de l'expression, MethodInfo methodInfo)
Nissan
17

Pour toute personne utilisant NSubstitute, j'ai pu modifier les réponses ci-dessus et faire quelque chose comme ça ... (où Details est le nom de la méthode Action sur le contrôleur)

 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
la source
13

Voici une solution de travail utilisant RhinoMocks. Je l'ai basé sur une solution Moq que j'ai trouvée sur 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
la source
5

Est AjaxRequest est une méthode d'extension. Vous pouvez donc le faire de la manière suivante en utilisant 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
la source
4

On dirait que vous cherchez ceci,

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

Utilisation dans le contrôleur:

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

Vous devez simuler HttpContextBase et le mettre dans votre propriété ControllerContext, comme ça:

controller.ControllerContext = 
new ControllerContext(mockedHttpContext, new RouteData(), controller);
Michał Chaniewski
la source
et de quoi mockedHttpContext aurait-il besoin d'être moqué? L'objet RequestContext dont il a besoin a besoin d'un objet HttpContextBase () dans le constructeur, et HttpContextBase () n'a pas de constructeur qui n'accepte aucun paramètre.
Nissan
J'ai essayé: 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 () as ViewResult; Cependant, obtenez toujours la même exception levée.
Nissan
Votre lien ne fonctionne pas, mais ce qui suit semble fonctionner _request.Setup (o => o.Form) .Returns (new NameValueCollection ());
Vdex
2

Pour rendre IsAjaxRequest()faux pendant le test unitaire, vous devez configurer les en-têtes de demande ainsi que la valeur de collecte de la demande dans votre méthode de test, comme indiqué ci-dessous:

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

La raison de la configuration des deux est cachée dans l'implémentation de IsAjaxRequest () qui est donnée ci-dessous:

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

Il utilise à la fois la collection de demandes et l'en-tête, c'est pourquoi nous devons créer une configuration pour l'en-tête et la collection de demandes.

cela rendra la demande de retour false quand ce n'est pas une demande ajax. pour le rendre vrai, vous pouvez faire ce qui suit:

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

J'ai trouvé un autre moyen d'ajouter un objet HttpRequestMessage dans votre demande pendant l'API Web comme suit

[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
la source
0

(Un peu tard à la fête mais je suis allé pour un itinéraire différent alors j'ai pensé partager)

Pour opter pour une méthode pure code / moqueuse de tester cela sans créer de simulations pour les classes Http, j'ai implémenté un IControllerHelper qui a une méthode Initialise qui prend la Request comme paramètre, puis expose les propriétés que je veux, par exemple:

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

Ensuite, dans mon contrôleur, j'appelle initialise au début de la méthode:

        _controllerHelper.Initialise(Request);

Et puis mon code ne dépend que de dépendances moquables.

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

Pour les tests fonctionnels, je remplace simplement iControllerHelper dans la composition pour un substitut.

Stu
la source