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.

This entry was posted in ASP.Net. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>