Unit Testing your MVC Views

The story around testing your UI JavaScript is getting better and better.  I’ve been using QUnit for a couple of years and it’s not hard to to test individual components of your UI.  One thing that has always bothered me is that a typical testing setup is to have part of your html markup in your test page that you run your tests against.  This is obviously not very DRY and I tend to refactor my HTML frequently while working, getting closer to semantic markup style and constantly cleaning up and fine tuning the UI.

What I really wanted was to render my actual markup in my test file and run my tests against that.  Since all of my views are strongly typed, it would be cool if I could create test data and inject that into the rendered HTML too.  That way I can test the view logic as well.

It turned out not to be too hard to do this.  I created an HTMLHelper extension called RenderViewUnderTest.  It takes a controller name, a view name and a view model you want passed in.  It will then render the view action with the test data within your test page.  It’s a hybrid between the RenderAction and RenderPartial methods currently available.

Here is some sample usage.

<div id="as-a-user">
   var data = new TimeCardViewModel()

                    CanEdit = true,
                    DateSelected = DateTime.Today,
                    UsersList = new[] { new SelectListItem { Text = "User", Value = "1" } },
                    TimeCardHistory = new[]{new TimeCardHistoryDto{ClockedDateTime = DateTime.Today.AddHours(6),Event = TimeCardEvent.StartOfDay,TimeCardEntryId = 1,UserId = 1}, 
                                              new TimeCardHistoryDto{ClockedDateTime = DateTime.Today.AddHours(12),Event = TimeCardEvent.StartOfLunch,TimeCardEntryId = 2,UserId = 1}, 
                                              new TimeCardHistoryDto{ClockedDateTime = DateTime.Today.AddHours(12).AddMinutes(30),Event = TimeCardEvent.EndOfLunch,TimeCardEntryId = 3,UserId = 1}, 
                                              new TimeCardHistoryDto{ClockedDateTime = DateTime.Today.AddHours(18).AddMinutes(30),Event = TimeCardEvent.EndOfDay,TimeCardEntryId = 4,UserId = 1}}

 <%= Html.RenderViewUnderTest("TimeCard", "Index", data) %>
 <%=Html.Script("~/Scripts/Tests/TimeCardTests") %>

Now you can run test this with your favorite JavaScript testing framework.

Some Caveats

I really consider this to be more of a proof of concept than a finished library.  Probably the biggest issue most people will have with this is that your test scripts live with your application.  The way the VirtualPathProvider in the built in view engine works really expects them to be in the same project.  I think it is possible to create a new PathProvider to overcome this, but it honestly doesn’t bother me enough to work on it.  In my project, I have one Controller and one route to handle tests and the rest is in the View folder.  My deployment script simply ignores Views/Tests folder so they don’t go to production.

While I’ve spiked this out and done some very complicated UI tests (drag and drop, micro templating, lots of AJAX), We have not yet incorporated this into our build or CI.  That is something I definitely need to work on.

More importantly, I’ve found that my biggest problem is that I’m not good at writing testable JavaScript.  I’m fixing that now, more to come.

The source code is available here if your interested.  I want to point out that it was relatively easy to do this because the MVC source code was available for me to review and poke at.  Like I said, I basically combined what the RenderAction and RenderPartial were doing.  Because the source code was available I did this in about 2 hours.  Otherwise it would have taken 3 times as long.

Installing MonoTouch