views:

502

answers:

2

Hi folks,

I'm doing some simple MS unit tests on my standard, nothing special controller.

When i check the ViewName proprty, from the returned ViewResult object, it's "" (empty).

I'm under the impression that the ViewName is implied by the name of the View (as suggested by this MS article on ASP.NET MVC controller testing).

BTW, when i test the ViewData, it's all there and correct.

Here's the code i have...

public ActionResult Index(int? page, string tag)
{
    if (page == null || page <= 0)
    {
        page = 1;
    }

    var viewData = new IndexViewData
                       {
                       ... my property setters, etc ...
                       };
    return View(viewData);
}

[TestMethod]
public void Index_Action_Should_Return_Index_View_For_Default_HomePage()
{
    // Arrange.
    var controller = PostController; // Wrapper, cause I use D.I.

    // Act.
    ViewResult viewResult = controller.Index(null, null) as ViewResult;

    // Assert.
    Assert.IsNotNull(viewResult);
    Assert.AreEqual("Index", viewResult.ViewName); // This is false/fails.

    var indexViewData = viewResult.ViewData.Model as IndexViewData;
    Assert.IsNotNull(indexViewData); // This is true.
}
+6  A: 

The ViewName is only present when you set it in the ViewResult. If your View name matches your controller name, then I would check to ensure that the ViewName is null or empty as that would be (IMO) the correct behavior since you wouldn't want to set a name on the view. I only check that the ViewName is set when I intend that the View to be returned does not match the action -- say, when returning the "Error" view, for example.

EDIT: The following is the source for ExecuteResult in ViewResultBase.cs (from RC1, I don't have the source for RTW on my Macintosh). As you can see it checks to see if the ViewName has been set directly and if not, it pulls it from the action in the controller context's route data. This only happens in ExecuteResult, which is invoked AFTER your controller's action has completed.

    public override void ExecuteResult(ControllerContext context) {
        if (context == null) {
            throw new ArgumentNullException("context");
        }
        if (String.IsNullOrEmpty(ViewName)) {
            ViewName = context.RouteData.GetRequiredString("action");
        }

        ViewEngineResult result = null;

        if (View == null) {
            result = FindView(context);
            View = result.View;
        }

        ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
        View.Render(viewContext, context.HttpContext.Response.Output);

        if (result != null) {
            result.ViewEngine.ReleaseView(context, View);
        }
    }
tvanfosson
Heya. Hmm. i don't get what you're saying. "The ViewName is only present when you set it in the ViewResult". I thought the framework auto sets this? Are you saying that it's not set automatically?
Pure.Krome
Nope. As far as I can tell the ViewName is null unless you specifically set it. I believe that if it is null or empty it pulls the view name from the name of the action.
tvanfosson
>>I believe that if it is null or empty it pulls the view name from the name of the action.<< Hmm.. so that means it shouldn't be null, right?
Pure.Krome
No. What I mean is that in order to choose the view it checks if the ViewName in the ViewResult is null or empty and, if it is, it chooses the view with the same name as the action. ViewName will be null unless you specifically set it in the ViewResult. I'll post the relevant code from the source
tvanfosson
AHHHHHHHHHHHHHH!!!! I gotcha now :)so if i did return View("Index", viewdata), then the viewname would be set! becuase (in my original example) i just returned the viewdata, it figured out the view via the name of the method! AH! gotcha! :)
Pure.Krome
A: 

The viewname is set automatically by the framework. But when we unit test, we short-circuit the framework and there is nothing left to set the name.

So our actions need to set the viewname explicitly when we unit test. We could also check for null or empty if we really, really want to lean on the convention.

Thomas Eyde