views:

2612

answers:

5

I have a controller in C# using the ASP.Net MVC framework

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

I got some tips on mocking and was hoping to test the code with the following and 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);

However I keep getting this error:

Exception System.ArgumentNullException: System.ArgumentNullException : Value cannot be null. Parameter name: request at System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest(HttpRequestBase request)

Since the Request object on the controller has no setter. I tried to get this test working properly by using recommended code from an answer below.

This used Moq instead of RhinoMocks, and in using Moq I use the following for the same 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);

but get the following error:

Exception System.ArgumentException: System.ArgumentException : Invalid setup on a non-overridable member: x => x.Headers["X-Requested-With"] at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo methodInfo)

Again, it seems like I cannot set the request header. How do I set this value, in RhinoMocks or Moq?

+1  A: 

You need to mock HttpContextBase and put it into your ControllerContext property, like that:

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

Here you can see how to mock the Form collection, your scenario will be similar: Mocking the HttpRequest in ASP.NET MVC

Michał Chaniewski
and what would mockedHttpContext need to be mocked? tje RequestContext object it requires needs an HttpContextBase() object in the constructor, and HttpContextBase() has no constructor that accepts zero parameters.
Nissan
Just use a mocking framework, don't try to construct those objects directly...
Michał Chaniewski
I tried: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;However still get the same exception thrown.
Nissan
+12  A: 

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

UPDATED:

Mock Request.Headers["X-Requested-With"] or Request["X-Requested-With"] instead of Request.IsAjaxRequest()

eu-ge-ne
I get the message "The Type argument for method 'ISetupGetter<T, TProperty>Moq.Mock<T>.SetupGet<Tpropert>.... cannot be infered from uage. Try specifying the type arguments explicitly.What type do I set 'var request=' to though to get this to work?
Nissan
Just updated my answer - not Request.IsAjaxRequest but Request.IsAjaxRequest(). Update your question too
eu-ge-ne
Still generates:Exception System.ArgumentException: System.ArgumentException : Invalid setup on a non-overridable member: x => x.IsAjaxRequest() at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo methodInfo)
Nissan
The problem is that IsAjaxRequest() is static extension method and cannot be mocked - I've updated my answer.
eu-ge-ne
should be context.SetupGet(x => x.Request).Returns(request.Object); your code above is missing the 's' on Returnstill Also results in Exception System.ArgumentException: System.ArgumentException : Invalid setup on a non-overridable member: x => x.Headers["X-Requested-With"] at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo methodInfo) error message
Nissan
@Nissan - Thanks, I updated my answer - this should work
eu-ge-ne
It does! Thanks for the help!
Nissan
Does anyone know how I can mock this when I need it to return false? I tried setting ["X-Requested-With"] to null or removing the mock all together but it fails
Damien
+2  A: 

Is AjaxRequest is an extension method. So you can do it the following way using 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
A: 

try this

danfromisrael
A: 

Here is a working solution using RhinoMocks. I've based it on a Moq solution I found at 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