0

Following is the code snippet for which I want to write unit tests:

[HttpGet]
public ActionResult Edit(string id)
{
    if (Request.IsAjaxRequest())
    {
        EditModel model = new EditModel();    
        .....
    }
    return View();
}

I want to write unit tests for this action where I can fake the result of Request.IsAjaxRequest() to true so that I can write tests for rest of the code of the action.

I have tried following but it doesn't work. _request.Headers is always empty, and Request.IsAjaxRequest() is always returning false:

[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled()
{
    HttpRequestBase _request = A.Fake<HttpRequestBase>();
    _request.Headers.Add("X-Requested-With", "XMLHttpRequest");
    _controller.ControllerContext = A.Fake<ControllerContext>();
    _controller.ControllerContext.HttpContext = _request;

    A.CallTo(() => _controller.Request).Returns(_request); 

    var result = _controller.Edit(1) as RedirectToRouteResult;
}

I always get Request.IsAjaxRequest() as false. Any help on this much appreciated. Thanks

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
Nirman
  • 6,715
  • 19
  • 72
  • 139
  • I'm trying to reproduce, but having big problems. I'm not a MVC guy, and maybe part of my problem is that I'm not using the right version (I'm using whatever came with VS2013), but `_controller.ControllerContext.HttpContext = _request;` doesn't compile (`HttpRequesteBase` isn't a `RequestContext`) and `_controller.Edit(1)` doesn't compile because `Edit` takes a `string`. Can you either point out where I'm going wrong or fix the question so it at least compiles? – Blair Conrad Oct 16 '15 at 10:41

1 Answers1

2

I managed to muddle past the compilation errors and use some information from Chapter 10 of FakeItEasy Succinctly, which is all about ASP.NET MVC.

Generally speaking, the ASP.NET MVC classes are not designed in a way to make them easily fakeable, but I have a test setup that causes IsAjaxRequest to return true. The two main hurdles were getting the controller to use the request object and to make sure that the request object was returning the headers we wanted. The first part was not hard, but the second required us to have the request object use a concrete NameValueCollection. The faked one that it had been providing by default was not useful, because the right properties weren't virtual. Fortunately, using a real NameValueCollection did the trick.

Try this:

[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled_Blair()
{
    HttpRequestBase _request = A.Fake<HttpRequestBase>();

    // NameValueCollection is effectively unfakeable due to non-virtual properties,
    // but a real one works just fine, so make sure the headers use one of those.
    A.CallTo(() => _request.Headers).Returns(new NameValueCollection());
    _request.Headers["X-Requested-With"] = "XMLHttpRequest";

    var httpContext = A.Fake<HttpContextBase>();
    A.CallTo(() => httpContext.Request).Returns(_request);

    _controller.ControllerContext = new ControllerContext(
        new RequestContext(httpContext, new RouteData()),
        _controller);

    var result = _controller.Edit(1) as RedirectToRouteResult;
}

Be warned that there will be lots of pitfalls like this in the MVC framework, and continuing to fake them may continue to be frustrating. You may find a more sustainable approach is to extract as much of your logic as is feasible out into plain old testable business classes that don't rely on the MVC framework.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • 1
    Thanks Blair for providing the solution.. and for the reference of the book.. that's very helpful... – Nirman Oct 19 '15 at 05:18