Search code examples
c#asp.net-mvcrhino-mocksmspec

Mocking and Stubbing Ajax Requests


Yesterday I re-factored the below method to return either a full view or a partial view.

public ActionResult List(int page)
{
    var viewModel = GetListViewModel(page);

    if(Request.IsAjaxRequest())
    {
        return PartialView("_list", viewModel);
    }
     return View("PartsList", viewModel);
}

But now my tests have broken, they are failing on the if statement. I've done a google and found you can mock/stub the HTTP Requests with something like,

HttpContextBase mockedContext = MockRepository.GeneratePartialMock<HttpRequestBase>();
HttpRequestBase mockedContext = MockRepository.GeneratePartialMock<HttpContextBase>();

mockedContext.Stub(x => x.Request).Return(mockedRequest);
mockedRequest.Stub(r => r["X-Requested-With"]).Return("");

subject.ControllerContext = new ControllerContext { HttpContext = mockedContext };

I implemented the above in my test and it is still falling over.

TEST

public class when_asked_for_the_parts_list_view : context_for_part_controller
{
    static ActionResult _result;
    static IPagination<Part> _parts;
    static PartListPageViewModel _partListPageViewModel;

    Establish context = () =>
    {
         mockedContext.Stub(x => x.Request).Return(mockedRequest);
         mockedRequest.Stub(r => r["X-Requested-With"]).Return("XMLHttpRequest");
         subject.ControllerContext = new ControllerContext { HttpContext = mockedContext };

         _parts = new List<Part>().AsPagination(1);
         _partListPageViewModel = new PartListPageViewModel();

         _partTasks.Stub(x => x.GetParts(1)).Return(_parts);
         _listMapper.Stub(x => x.MapFrom(_parts)).Return(_partListPageViewModel);
    };

    Because of = () =>
    {
        _result = subject.List(1);
    };

    It should_retreve_the_parts =
        () => _partTasks.AssertWasCalled(x=>x.GetParts(1));

    It should_map_the_parts_to_a_viewModel =
        () => _listMapper.AssertWasCalled(x => x.MapFrom(_parts));

    It should_return_the_list_as_a_partialview =
        () => _result.ShouldBeAPartialView().ViewData.Model.ShouldEqual(_partListPageViewModel);
}

ERROR

should map the parts to a viewModel : Failed
The method or operation is not implemented.

System.NotImplementedException: The method or operation is not implemented.

at System.Web.HttpRequestBase.get_Headers()
at HttpRequestBaseProxy5a253d42dd57477f936e24736e848cbb.get_Headers_callback_47()
at HttpRequestBaseProxy5a253d42dd57477f936e24736e848cbb.Invocationget_Headers_66.InvokeMethodOnTarget()
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Rhino.Mocks.Impl.ReplayPartialMockState.DoMethodCall(IInvocation invocation, MethodInfo method, Object[] args)
at Rhino.Mocks.Impl.ReplayMockState.MethodCall(IInvocation invocation, MethodInfo method, Object[] args)
at Rhino.Mocks.MockRepository.MethodCall(IInvocation invocation, Object proxy, MethodInfo method, Object[] args)
at Rhino.Mocks.Impl.RhinoInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at HttpRequestBaseProxy5a253d42dd57477f936e24736e848cbb.get_Headers()
at System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest(HttpRequestBase request)
at Catalogue.Web.Controllers.Part.PartController.List(Int32 page) in PartController.cs: line 143
at Catalogue.MSpecTests.UI.Parts.when_asked_for_the_parts_list_view.<.ctor>b__6() in PartControllerTests.cs: line 214

How do I get the request in the Request.IsAjaxRequest() in my controller to pass as an ajax request???

Regards Rich

Edit - Found this post, my ajax test is now passing but my non-ajax is still failing.


Solution

  • You should probably create an interface like this, which is easy to implement and mock:

    public interface IRequestInfo
    {
        bool IsAjaxRequest { get; }
    }
    

    Then your class might look like this:

    public class MyClass
    {
        private readonly IRequestInfo _request;
    
        public MyClass(IRequestInfo request)
        {
            _request = request;
        }
    
        public ActionResult List(int page)
        {
            var viewModel = GetListViewModel(page);
            if (_request.IsAjaxRequest)
            {
                return PartialView("_list", viewModel);
            }
            return View("PartsList", viewModel);
        }
    }
    

    And your tests becomes:

    [Test]
    public void List_returns_PartialView_for_Ajax_request()
    { 
       // arrange system under test and stubs
       var request = MockRepository.GenerateStub<IRequestInfo>();
       request.Stub(x => x.IsAjaxRequest).Returns(true);
       var myObject = new MyClass(request);
    
       // act
       object result = myObject.List(0);
    
       // assert
       Assert.IsTrue(result is PartialView);
    }
    

    Note how your test is no longer concerned with HTTP headers. Those are an internal concern of the IRequestInfo implementation. And since that implementation will just be a thin wrapper around the ASP.NET HttpContext.Request object, there is no need to unit test it.