Como zombar da solicitação no controlador no asp.net MVC?

171

Eu tenho um controlador em c # usando a estrutura do asp.net MVC

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

Eu recebi algumas dicas sobre zombaria e esperava testar o código com o seguinte 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);

No entanto, continuo recebendo este erro:

Exceção System.ArgumentNullException: System.ArgumentNullException: O valor não pode ser nulo. Nome do parâmetro: request em System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest (solicitação HttpRequestBase)

Como o Requestobjeto no controlador não possui setter. Tentei fazer esse teste funcionar corretamente usando o código recomendado de uma resposta abaixo.

Isso usava o Moq em vez do RhinoMocks e, ao usar o Moq, uso o seguinte para o mesmo teste:

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

mas obtenha o seguinte erro:

Exceção System.ArgumentException: System.ArgumentException: instalação inválida em um membro não substituível: x => x.Headers ["X-Requested-With"] em Moq.Mock.ThrowIfCantOverride (instalação da expressão, MethodInfo methodInfo)

Mais uma vez, parece que não consigo definir o cabeçalho da solicitação. Como faço para definir esse valor no RhinoMocks ou no Moq?

Nissan
fonte
Substituir Request.IsAjaxRequest por Request.IsAjaxRequest ()
eu-ge-ne
Simule Request.Headers ["X-Requested-With"] ou Request ["X-Requested-With"] em vez de Request.IsAjaxRequest (). Eu atualizei minha pergunta
eu-ge-ne
tente isso #
danfromisrael

Respostas:

211

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

ATUALIZADA:

Zombe Request.Headers["X-Requested-With"]ou em Request["X-Requested-With"]vez de Request.IsAjaxRequest().

eu-ge-ne
fonte
1
Recebo a mensagem "O argumento Type para o método 'ISetupGetter <T, TProperty> Moq.Mock <T> .SetupGet <Tpropert> .... não pode ser inferido a partir do uage. Tente especificar explicitamente os argumentos de tipo. Que tipo eu defino 'pedido var =' para que para chegar a este trabalho?
Nissan
Acabei de atualizar minha resposta - não Request.IsAjaxRequest, mas Request.IsAjaxRequest (). Atualize também a sua pergunta
eu-ge-ne
Ainda gera: excepção System.ArgumentException: System.ArgumentException: configuração inválido em membro não-substituível: x => x.IsAjaxRequest () em Moq.Mock.ThrowIfCantOverride (configuração Expressão, MethodInfo- MethodInfo-)
Nissan
O problema é que IsAjaxRequest () é um método de extensão estática e não pode ser ridicularizado - atualizei minha resposta.
eu-ge-ne
deve ser context.SetupGet (x => x.Request) .Returns (request.Object); seu código acima está faltando o 's' no retorno ainda resulta em Exceção System.ArgumentException: System.ArgumentException: instalação inválida em um membro não substituível: x => x.Headers ["X-Requested-With"] no Moq .Mock.ThrowIfCantOverride mensagem de erro (configuração Expression, MethodInfo MethodInfo)
Nissan
17

Para quem usa o NSubstitute, eu consegui modificar as respostas acima e fazer algo assim ... (onde Detalhes é o nome do método Action no 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
fonte
13

Aqui está uma solução funcional usando o RhinoMocks. Baseei-o em uma solução Moq que encontrei em 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
fonte
5

Is AjaxRequest é um método de extensão. Então você pode fazer isso da seguinte maneira usando o 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
fonte
4

Parece que você está procurando por isso,

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

Uso no Controlador:

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

Você precisa zombar do HttpContextBase e colocá-lo em sua propriedade ControllerContext, assim:

controller.ControllerContext = 
new ControllerContext(mockedHttpContext, new RouteData(), controller);
Michał Chaniewski
fonte
e o que o mockedHttpContext precisa ser ridicularizado? O objeto RequestContext necessário requer um objeto HttpContextBase () no construtor e HttpContextBase () não possui um construtor que aceite zero parâmetros.
Nissan
Eu tentei: var mocks = new MockRepository (); var mockedhttpContext = mocks.DynamicMock <HttpContextBase> (); var mockedHttpRequest = mocks.DynamicMock <HttpRequestBase> (); SetupResult.For (mockedhttpContext.Request) .Return (mockedHttpRequest); var controller = new HomeController (Repositório, LoginInfoProvider); controller.ControllerContext = novo mockedhttpContext, novo RouteData (), controlador); var resultado = controller.Index () como ViewResult; No entanto, ainda recebe a mesma exceção lançada.
Nissan
Seu link não funciona, mas o seguinte parece funcionar _request.Setup (o => o.Form) .Returns (new NameValueCollection ());
Vdex 31/05
2

Para fazer IsAjaxRequest()retornar falso durante o teste de unidade, você precisa configurar os Cabeçalhos de solicitação e o valor da coleção de solicitação no método de teste, conforme indicado abaixo:

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

O motivo da configuração de ambos está oculto na implementação de IsAjaxRequest (), que é fornecida abaixo:

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

Ele usa a coleção de solicitações e o cabeçalho; é por isso que precisamos criar a configuração para a coleção de cabeçalhos e solicitações.

isso fará com que a solicitação retorne false quando não for uma solicitação ajax. para torná-lo verdadeiro, você pode fazer o seguinte:

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

Encontrei outra maneira de adicionar um objeto HttpRequestMessage à sua solicitação durante a API da Web, conforme a seguir

[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
fonte
0

(Um pouco tarde para a festa, mas eu segui uma rota diferente, então pensei em compartilhar)

Para ir para uma maneira pura de código / zombaria de testar isso sem criar zombarias para as classes Http, implementei um IControllerHelper que possui um método Initialise que usa a Solicitação como parâmetro e depois expõe as propriedades que desejo, por exemplo:

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

Então, no meu controlador, eu chamo initialize no início do método:

        _controllerHelper.Initialise(Request);

E então meu código depende apenas de dependências simuláveis.

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

Para testes funcionais, substituo o iControllerHelper na composição por um substituto.

Stu
fonte