Difficulties unit testing code which uses HtmlHelper.ActionList()

Last post 12-03-2008 9:49 PM by Neil McCrossin. 2 replies.

Sort Posts:

  • Difficulties unit testing code which uses HtmlHelper.ActionList()

    12-03-2008, 1:24 AM

    I have started using the HtmlHelper.ActionLink() method in an application (in the middle of some code that generates HTML based on database records) and it works fine, but I want to unit test the code that is using it. Therefore I don't want it to be in the view class, I want it to be in another class that is called by the view class and that is under unit tests. However, in order to construct an instance of the HtmlHelper class for use in the unit tests, I need a ViewContext (I can't see why ActionLink would need to know about the view in any way, but I guess other methods of HtmlHelper must need the ViewContext).

    The code below shows how I have attempted to do this (I am using NUnit). Lines 54 to 66 are where I construct the objects needed to construct the HtmlHelper. Lines 1 to 37 are classes I needed to implement in a minimal way in order for the code to compile and run without any exceptions. Lines 51 and 52 are where I have registered the routing information from the main application.

    This code runs but the ActionLink method does not work correctly - the url comes out empty:

    Expected: "...01 12:00 AM: Task 1</del> <a href="/Home/Delete/1">Delete</a>"
    But was: "...01 12:00 AM: Task 1</del> <a href="">Delete</a>"

    I have two questions:
    1) Is it really necessary to jump through all these hoops just to unit test code which uses ActionLink? Surely there must be an easier way.
    2) If there is not an easier way, what do I have to do to get this to work?

    1        public class HttpContextDummy : HttpContextBase
    2        {
    3            public override HttpRequestBase Request
    4            {
    5                get { return new HttpRequestDummy(); }
    6            }
    7        }
    8    
    9        public class HttpRequestDummy : HttpRequestBase
    10       {
    11           public override string AppRelativeCurrentExecutionFilePath
    12           {
    13               get { return "~/"; }
    14           }
    15   
    16           public override string PathInfo
    17           {
    18               get { return ""; }
    19           }
    20       }
    21   
    22       public class ViewDummy : IView
    23       {
    24           public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    25           {
    26               throw new NotImplementedException();
    27           }
    28       }
    29   
    30       public class ViewDataContainerDummy : IViewDataContainer
    31       {
    32           public ViewDataDictionary ViewData
    33           {
    34               get { throw new NotImplementedException(); }
    35               set { throw new NotImplementedException(); }
    36           }
    37       }
    38   
    39   
    40       [TestFixture]
    41       public class MarkAsCompletedTest
    42       {
    43           private HomeController m_controller;
    44           private HtmlHelper m_html;
    45   
    46           [SetUp]
    47           public void Init()
    48           {
    49               m_controller = new HomeController(new TasksArray());
    50   
    51               RouteCollection routes = new RouteCollection();
    52               MvcApplication.RegisterRoutes(routes);
    53   
    54               HttpContextBase httpContext = new HttpContextDummy();
    55               ViewContext viewContext =
    56                   new ViewContext
    57                   (
    58                       httpContext,
    59                       routes.GetRouteData(httpContext),
    60                       m_controller,
    61                       new ViewDummy(),
    62                       new ViewDataDictionary(),
    63                       new TempDataDictionary()
    64                   );
    65   
    66               m_html = new HtmlHelper(viewContext, new ViewDataContainerDummy());
    67           }
    68   
    
     
  • Re: Difficulties unit testing code which uses HtmlHelper.ActionList()

    12-03-2008, 5:02 PM
    Answer
    • Contributor
      2,092 point Contributor
    • jeloff
    • Member since 05-15-2008, 12:01 AM
    • Richmond, BC
    • Posts 382
    • AspNetTeam

    Hi

     Try changing the last line in your Init method to include the RouteCollection. So, instead of m_html = new HtmlHelper(viewContext, new ViewDataContainerDummy()); try

    m_html = new HtmlHelper(viewContext, new ViewDataContainerDummy(), routes);

     

    Yes, the process is somewhat complex, but if you generalize your setup code for these types of tests you only need to write it once and can then re-use it - that's what we did for the HtmlHelper test cases. As for the complexity: all paths from AcitonLink and RouteLink leads to an internal method that relies on using the GetVirtualPath method which requires a RequestContext and hence in MVC's case, a ViewContext is used.

    Jacques

  • Re: Difficulties unit testing code which uses HtmlHelper.ActionList()

    12-03-2008, 9:49 PM

    Thanks Jacques, that did the trick. Once I had got past that hurdle, I just had to add a few more methods to the dummy classes and it worked. Here is the final class I ended up with:

     

    using System;
    using System.Web;
    using System.Web.Routing;
    using System.Web.Mvc;
    
    namespace TaskList.Tests
    {
        class HtmlHelperFactory
        {
            public static HtmlHelper CreateInstance(Controller controller)
            {
                RouteCollection routes = new RouteCollection();
                MvcApplication.RegisterRoutes(routes);
    
                HttpContextBase httpContext = new HttpContextDummy();
    
                ViewContext viewContext =
                    new ViewContext
                    (
                        httpContext,
                        routes.GetRouteData(httpContext),
                        controller,
                        new ViewDummy(),
                        new ViewDataDictionary(),
                        new TempDataDictionary()
                    );
    
                return new HtmlHelper(viewContext, new ViewDataContainerDummy(), routes);
            }
    
    
            // Dummy classes needed to be able to create HtmlHelper
    
            private class HttpRequestDummy : HttpRequestBase
            {
                public override string ApplicationPath
                {
                    get { return ""; }
                }
    
                public override string AppRelativeCurrentExecutionFilePath
                {
                    // Any shorter string here gives exception:
                    // index larger than length of string
                    get { return "~/"; }
                }
    
                public override string PathInfo
                {
                    get { return ""; }
                }
            }
    
            private class HttpResponseDummy : HttpResponseBase
            {
                public override string ApplyAppPathModifier(string virtualPath)
                {
     	             return virtualPath;
                }
            }
    
            private class HttpContextDummy : HttpContextBase
            {
                public override HttpRequestBase Request
                {
                    get { return new HttpRequestDummy(); }
                }
    
                public override HttpResponseBase Response
                {
                    get { return new HttpResponseDummy(); }
                }
            }
    
            private class ViewDummy : IView
            {
                public void Render(ViewContext viewContext, System.IO.TextWriter writer)
                {
                    throw new NotImplementedException();
                }
            }
    
            private class ViewDataContainerDummy : IViewDataContainer
            {
                public ViewDataDictionary ViewData
                {
                    get { throw new NotImplementedException(); }
                    set { throw new NotImplementedException(); }
                }
            }
        }
    }
    
     
Page 1 of 1 (3 items)