Unit Testing Framework for MS MVC.NET
The Pain - A Brief History of MVC.NET
Files: MvcTestingFramework.rar - or - MvcTestingFramework.zip
Back in November, ScottGu gave us some great examples on how to use the (then unreleased) MVC Framework. A major advantage of this framework is to be in the ease of testing. One of the classes used in that demo was a TestViewEngine that was implemented to determine what variables were passed to RenderView and RedirectToAction.
Since then we've learned that it was some private class that wasn't in the framework. In December, Phil Hack recommended subclassing as a means to get at the RenderView which he admits leaves a certain bad taste in some people's mouths.
I'm one of those people.
He also went on to explain that it can be done through mocking with RhinoMocks but then changed his mind saying that it wouldn't build against the actual CTP version of the framework. (Thanks, you tease.) Then there was the concept of putting it in an extension method, but that prevents us from properly setting the stage for our tests.
There is also an issue with testing TempData in controllers even if you manage to get around the RenderView problems via subclassing. Ben Scheirman recommended doing this through mocking using SetupResult. Justice Gray then chimes in that something similar can be done for mocking Request.Form.
Seeing all this chaos, and knowing that there had to be a better way, I've combined a number of these recommendations into a framework that allows for easy and effective testing of ControllerActions.
It uses a bunch of Mocking, Reflection, and Dynamic Proxies to get the job done.
Sample Uses of the Framework
If you're like me you've already downloaded the rar file at the top of the page. Please direct your attention to the MvcTestingFramework.Sample.Test project in the StarsControllerTest.cs file. Here's you'll find some simple uses of the framework.
Using RenderView
[Test]
public void ListControllerSelectsListView()
{
MvcTestHandler handler = new MvcTestHandler();
StarsController controller = handler.CreateController<StarsController>();
controller.List();
Assert.AreEqual("List", handler.RenderViewData.ViewName);
}
Note that we're creating an instance of MvcTestHandler in this and every other test case in this class. This handler performs several functions, namely creating our Controllers and populating them correctly. We're also able to get at the parameters used in the RenderView method call by our ControllerAction via the RenderViewData class.
Using Redirect To Action
[Test]
public void AddFormStarShouldRedirectToList()
{
MvcTestHandler handler = new MvcTestHandler();
StarsController controller = handler.CreateController<StarsController>();
controller.AddFormStar();
Assert.AreEqual("List", handler.RedirectToActionData.ActionName);
}
Again, we're able to access the ActionName from the handler's RedirectToActionData class.
Using Request.Form and TempData
[Test]
public void AddFormStarShouldSaveFormToTempData()
{
MvcTestHandler handler = new MvcTestHandler();
StarsController controller = handler.CreateController<StarsController>();
handler.Form["NewStarName"] = "alpha c";
controller.AddFormStar();
Assert.AreEqual("alpha c", controller.TempData["NewStarName"]);
}
We simulate a form request, then read it back in from the controller's TempData member. Do that with a controller that you make via a standard constructor and you won't have such good results.
Comments on this Framework
The MvcTestingFramework relies on two great frameworks, the Castle DynamicProxy framework and the aforementioned RhinoMocks framework.
After establishing a proxy of the given controller, the existing RedirectToAction and RenderView methods are changed so that they save the parameters and don't actually redirect or render a view.
One particular annoying part (and there were several, saved only by The Reflector) was that a couple of objects types used by RedirectToAction are marked as internal, so I was forced to iterate over the properties looking for the Action and Controller properties. MS... Please, no more hoops and lions.
The one area that I don't have working is Session variables. (You'll note those unit tests fail.) Hopefully someone more familiar with Rhino Mocks will be able to get this working.

February 8th, 2008 at 12:03 am
You are the ANSWER TO MY PRAYERS.
This is almost exactly what I was thinking about last night!!
Wonderful!
February 8th, 2008 at 12:04 am
BTW, I don't care for the subclassing method either as it does not scale. I too was a little bit annoyed once getting the initial CTP on finding there was no real test view engine or the like. I'm hoping for some surprises in the next drop.
February 8th, 2008 at 7:10 am
Tried to download but I am getting a 404 error.
February 8th, 2008 at 7:43 am
Seriously cool stuff. Good job!
February 8th, 2008 at 9:00 am
If anyone else gets a 404 or other error please let me know. I've tried it from two locations using two browsers and it worked perfectly every time.
February 8th, 2008 at 4:25 pm
The 404 is experienced if clicked into the blog, it does not occur from the front page.
I think your links must be relative.
February 8th, 2008 at 4:39 pm
@Troy
Fixed. I feel stupid...