Html.DisplayFor helper does not render values (during testing)

Nov 13, 2011 at 10:49 PM

Hi David, first allow me to thank you for this great endeavor! 

It must be me somehow not following the instructions, but in my simple demo solution consisting of two projects (Mvc3, and UnitTest) I am unable to test the model values that I am expecting to be rendered.

The pre-compiled view [strongly typed] has been generated by right-clicking the controller Action. Once created I have adjusted the Custom Tool (RazorGenerator) and Build Action (None) properties as described in your post: http://blog.davidebbo.com/2011/06/unit-test-your-mvc-views-using-razor.html.

In my unit test project the RenderAsHtml( ) method returns the document object ok, which I can successfully query but the model values are nowhere to be found. I actually get the following boilerplate InnerHtml text inside the "td" tags:

<td>
   /* DisplayTemplates/String */
</td>
...

Would this be due to some misconfiguration/omission on my part (apologies!) or it is something unsupported when executing view from within a unit test project? I am really sorry to bother you with this nonsense, but I am completely stuck here for 2 days. Thank you very much!

 

Below is my code snippet, along with the View markup in order to have a complete picture.

Unit Test File: ShowroomViewsTest.cs

 

var controller = new ShowroomController();
// The model object value is hardcoded inside the Index action for demo purposes
ViewResult result = (ViewResult)controller.Index();

var view = new WarehouseViews.Index();
HtmlDocument doc = view.RenderAsHtml((IEnumerable<Mvc3AndRazorGenerator.Models.Display.ShowroomModel>)result.ViewData.Model);

 

 

View File: Index.cshtml

 

@model IEnumerable<Mvc3AndRazorGenerator.Models.Display.ShowroomModel>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table id="pageContentIndexTable">
    <tr>
        <th>Name</th>
        <th>City</th>
        <th>Capacity</th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.City)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Capacity)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}
</table>

 

 

Coordinator
Nov 14, 2011 at 7:25 AM

This may seem strange, but the behavior is actually deliberate. See "What about partial views?" section in my post. Here, you're using a display template, but that's essentially a special case of partial view.

That being said, there may be a better way we can handle these scenarios. Let's step back here. What are you trying to test:

  1. The rendering details of the display template? OR
  2. The values you're passing to it?

If #1, then I would argue the display template should be unit tested on its own.

If #2, maybe what we can do is change the 'dummy' rendering (e.g. /* DisplayTemplates/String */) to include the passed in model data (e.g. in a JSON like format). This way you can verify that the right data is being shown, but without formatting concern.

Thoughts? If you want to experiment with this, look at DummyView in RazorGenerator.Testing\WebViewPageExtensions.cs:

            public void Render(ViewContext viewContext, TextWriter writer)
            {
                // Render a marker instead of actually rendering the partial view
                writer.WriteLine(String.Format("/* {0} */", ViewName));
            }

Here, you can get the model from the viewContext and potentially render something.

 

DummyView
Nov 19, 2011 at 9:33 AM

Wow, your suggestion #2 is ... awesome!!!

Indeed, I am not concerned about the formatting all I need to see is the values (usually represented by value-types and strings). I have modified the code locally as illustrated below and voila - I can test my views.

Thank you so much David for RazorGenerator in general and for your great responce specifically. With this change I can proceed with the project. 

BTW I have left a suggestion to replace WATIN with RazorGenerator in MSDN Magazine article on BDD (Behavior-Driven Development with SpecFlow by Brandon Satrom, December 2010 Issue).

public void Render(ViewContext viewContext, TextWriter writer)
{
    // Render a marker instead of actually rendering the partial view
    writer.WriteLine(String.Format("/* {0} [{1}] */", 
                        ViewName, 
                        ContainsModel(viewContext) ? viewContext.ViewData.Model : String.Empty));
}

private bool ContainsModel(ViewContext viewContext)
{
    // A bit of defensive programming
    return (viewContext != null &&
            viewContext.ViewData != null &&
            viewContext.ViewData.Model != null);
}

Coordinator
Nov 23, 2011 at 7:17 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Nov 23, 2011 at 7:18 AM

Great, I opened a bug for it. Feel free to send a pull request for it if your change is working well! :)