nik codes

Archive for the month “September, 2013”

Testing MVC View Names with [CallerMemberName]

We’ve all written ASP.NET MVC controller actions that simply return a view. They tend to look just like this:

public ActionResult Index()
{
   
return View(); }

It’s also fairly common to test those actions to ensure that the proper view is being returned:

public void Should_Return_Index_View(HomeController sut)
{
    
var
actionResult = sut.Index();
    
var viewResult = actionResult as ViewResult
;
   

Assert
.Equal("Index", viewResult.ViewName); }

If you’ve tried this before you know that the above test will fail. Why? Because viewResult.ViewName actually has a value of string.Empty, not Index. MVC doesn’t mind this, because if a view’s name is empty, it simply defaults to use the value of {action} from the route data collection. That unfortunately makes it difficult to ensure the proper view is being returned in tests.

We could re-work our action method to explicitly specify the name of the view returned (return View(“Index”)), but I really like the simplicity of having a default convention for view names. Instead I’ve been leveraging C# 5.0’s new caller information attributes to retain the simplicity of the convention, but still have the view name explicitly set for testing purposes.

To do this I’ve added the following two methods to the base controller that all of my project’s controllers inherit from:

public new ActionResult View([CallerMemberName] string actionMethod = null)
{
    
return base
.View(actionMethod); } public ActionResult View(object model, [CallerMemberName] string actionMethod = null) {
    
return base.View(actionMethod, model); }

Now, when I re-execute the test, it passes. That’s because the actionMethod parameter is automatically set by the compiler to be the name of the calling method, which in this case is Index. I can still pass in an overriding view name whenever I want, and the compiler will just get out of the way. Nifty!

NOTE: I will point out that you may want to consider if you even need to test the name of a ViewResult to begin with. Assuming you do though, I find this technique to be much cleaner than trying to mock out a whole request just to see what the {action} is.

Advertisements

Post Navigation