Mocking and testing for RedirectToAction

Rate It (1)

Last post 03-12-2008 11:10 PM by kbaley. 6 replies.

Sort Posts:

  • Mocking and testing for RedirectToAction

    03-09-2008, 9:18 AM
    • Loading...
    • CVertex
    • Joined on 09-09-2006, 2:53 AM
    • Posts 89

    Howdy,

    I just upgraded my little playground web app to Preview 2. I wrote alot of code using the first preview, so it's taken me an hour or two to completely upgrade.

    However, my controller tests fail :( Reliably, it's the controller tests that are asserting that RedirectToAction was called.

    Cos preview 2 supports controller mocking, I ditched the sub-class testing patterny thing and opted for the mocking.

    Here's one of my controller actions that uses RedirectToAction:

     

     

    1    public void Create(string Message) {
    2    
    3                if (String.IsNullOrEmpty(Message)) {
    4                    throw new ArgumentException("Message cannot be null or empty");
    5                }
    6    
    7                var rm = new ReminderMessage();
    8                rm.Message = Message;
    9    
    10               
    11   
    12               db.ReminderMessages.InsertOnSubmit(rm);
    13               db.SubmitChanges();
    14   
    15               RedirectToAction("List" );
    16           }
    
    For the ReminderMessagesController
    My error unit tests run fine, cos they're exception expecting.
    But, my normal operation unit test fails,
      
    [Test]
            [RollBack]
            public void CreateNormally()
            {
                var rmc = Controller();
                using (mocks.Record()) {
                    mocks.SetFakeControllerContext(rmc);
                }
                using (mocks.Playback()) {
                    rmc.Create("Sample Message");
                }
            }
     
     with the MbUnit test execution exception:
    ReminderMessagesControllerTests.SetUp.UpdateMessage


    Type:System.NotImplementedException
    Message:The method or operation is not implemented.
    Source:System.Web.Abstractions
    TargetSite:Void Redirect(System.String)
    HelpLink:null
    StackTrace:
       at System.Web.HttpResponseBase.Redirect(String url)
    at HttpResponseBaseProxy2ad1c711cecc4e6da524bc5c50bdce13.Redirect_callback_20(String url)
    at HttpResponseBaseProxy2ad1c711cecc4e6da524bc5c50bdce13.InvocationRedirect_32.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 HttpResponseBaseProxy2ad1c711cecc4e6da524bc5c50bdce13.Redirect(String url)
    at System.Web.Mvc.Controller.RedirectToAction(RouteValueDictionary values)
    at System.Web.Mvc.Controller.RedirectToAction(String actionName)
    at CN.Web.Controllers.ReminderMessagesController.Update(Int32 id) in C:\Projects\CharlesNicholas\CN.Web\Controllers\ReminderMessagesController.cs:line 78
    at CN.Web.Test.ReminderMessagesControllerTests.UpdateMessage() in C:\Projects\CharlesNicholas\CN.Web.Test\ReminderMessagesControllerTests.cs:line 141
     

    Woh. that's a deep stack trace. I'm not exactly sure what's going on here, but I bet it has something to do with redirects trying to call a 301 on some part of System.Web that wasn't mocked.

    Okay, so how do I mock RedirectToAction when it isn't public? I thought all of these actionable methods were going to be public to avoid the subclass testing patterny thingo - which involved alot of duplicated code.

    Does someone know what I'm doing wrong?

    Thanks,
    -CV

     

     


     

  • Re: Mocking and testing for RedirectToAction

    03-09-2008, 11:22 AM
    • Loading...
    • tgmdbm
    • Joined on 12-17-2007, 9:08 AM
    • Posts 684
    • ASPInsiders

    You  are obviously creating Partial Mocks. What that means is, if you don't setup an expectation on the mocked object for a particular method, then it will defer to the mocked objects original implementation.

            [Test]
    [RollBack]
    public void CreateNormally()
    {
    var rmc = Controller();

    using ( mocks.Record() ) {
    mocks.SetFakeControllerContext( rmc );
    Expect.Call( rmc.Response.Redirect( null ) )
    .Constraints( Text.EndsWith( "List.aspx" ) );
    }

    using ( mocks.Playback() ) {
    rmc.Create( "Sample Message" );
    }
    }

    The Expect.Call will setup the expectation on the Response mock object, and the call to Contraints will ignore the null argument and test that it ends with "List.aspx". You could use Text.Contains( "List" ) but that would break if "list" apeared anywhere else in the Url. You could of course implement your own AbstractContraint.

     

    Hope this helps 

  • Re: Mocking and testing for RedirectToAction

    03-12-2008, 4:16 AM
    • Loading...
    • CVertex
    • Joined on 09-09-2006, 2:53 AM
    • Posts 89

    Howdy,
     

    Thanks for your response.

    I am using partial mocks. I'm using ScottHa's code he presented on his blog.

    I couldn't get your code to compile unless I wrapped the rmc.Response.Redirect(null) in a lambda, i.e. () => rmc.Response.redirect(null)

    When I do that, the constraint always fails. Rhino Mocks says that null is indeed passed to the Response.Redirect method. There's is little I can do to make this pass.

    Phil mentioned on his blog that this was a bug. Just out of curiosity? Where is the bug here? Is it that RedirectToAction is NOT public (i.e. mockable)?

    Excuse my ignorance, I'm still wrapping my head around Rhino mocks.

     
    Regards,

    -CV
     

  • Re: Mocking and testing for RedirectToAction

    03-12-2008, 5:35 AM
    • Loading...
    • tgmdbm
    • Joined on 12-17-2007, 9:08 AM
    • Posts 684
    • ASPInsiders

    What was the compiler error? 

  • Re: Mocking and testing for RedirectToAction

    03-12-2008, 9:48 AM
    • Loading...
    • CVertex
    • Joined on 09-09-2006, 2:53 AM
    • Posts 89

    I wasn't on the same machine as I wrote the other code, but I quickly set up a similar situation and the same compiler error. 

    Here are the compiler errors: 

    Error    1    The best overloaded method match for 'Rhino.Mocks.Expect.Call(Rhino.Mocks.Expect.Action)' has some invalid arguments    D:\Projects\MvcExperimentTests\Controllers\HomeControllerTest.cs    59    17    MvcExperimentTests

    Error    2    Argument '1': cannot convert from 'void' to 'Rhino.Mocks.Expect.Action'    D:\Projects\MvcExperimentTests\Controllers\HomeControllerTest.cs    59    29    MvcExperimentTests

     

    Should I wrap the rmc.Response.Redirect(null) call in a lambda?

    or call Expect.Call<Action<string>>(rmc.Response.Redirect)?

    In either case, Reponse.Redirect always gets passed null, and the Text.EndsWith("List.aspx") constraint never passes. Even Is.NotNull() fails.

     

     


     

  • Re: Mocking and testing for RedirectToAction

    03-12-2008, 9:55 AM
    • Loading...
    • CVertex
    • Joined on 09-09-2006, 2:53 AM
    • Posts 89

    Phil mentions the RedirectToAction testing problem on his blog.

    Is there a friendly work around for now? Just so my tests pass?
     

  • Re: Mocking and testing for RedirectToAction

    03-12-2008, 11:10 PM
    Answer
    • Loading...
    • kbaley
    • Joined on 06-12-2006, 9:33 PM
    • Posts 13

    Yes, you need to wrap the call in a lambda. I think Ayende talks about this on his blog.

    Also, you can't test for List.aspx because there's a good chance the URL won't end in .aspx. But even if you test for something valid, I haven't been able to get it to work. The SetFakeControllerContext creates a context with an empty RouteData which is where the null is coming into play. Somewhere in the bowels of the Route class, it's referencing a property on the HttpContextBase.Request method that hasn't been mocked (AppRelativeCurrentExecutionFilePath I think).

    Not sure what the way to do this now is. I don't want to go back to a test-specific subclass just for the RedirectToAction method. At this point, I'm almost inclined to simply not test calls to RedirectToAction and add comments to add tests when it becomes possible.

Page 1 of 1 (7 items)
Microsoft Communities
Page view counter