The Unit Testing Framework I posted a couple of days ago has now been moved into the MvcContrib project, making me an official Developer. Woo Hoo. You can check out the updated documentation.
February 20, 2008
IParseable Interface
[Note: I was reminded by my roommate after posting this that static methods aren't allowed in interfaces....]
Today I was working on a JSON to NHibernate bridge when I came across something quite frustrating when trying to convert strings to other types. In this bridge object I’m persisting an object out of the database with NHibernate, then assigning the new values from the JSON string to that object. (Or list of objects.)
This seemed simple enough. I was thinking that it should be easy enough to write a loop that looks at the javascript keys {“Key”:”Value”} and matches them up with the PropertyInfo of the target object. This is trivial when both are strings, but what if the target type is a float, int, bool, date, enum etc?
Most base types have a Parse method of some kind that looks like Parse(string) or Parse(string, IFormatProvider). Here’s a couple of types that implement some variant of the Parse method.
- System.Boolean
- System.Byte
- System.Char
- System.Data.SqlTypes.SqlBoolean
- System.Data.SqlTypes.SqlByte
- System.Data.SqlTypes.SqlDateTime
- System.Data.SqlTypes.SqlDecimal
- System.Data.SqlTypes.SqlDouble
- System.Data.SqlTypes.SqlGuid
- System.Data.SqlTypes.SqlInt16
- System.Data.SqlTypes.SqlInt32
- System.Data.SqlTypes.SqlInt64
- System.Data.SqlTypes.SqlMoney
- System.Data.SqlTypes.SqlSingle
- System.DateTime
- System.DateTimeOffset
- System.Decimal
- System.Double
- System.Int16
- System.Int32
- System.Int64
- System.SByte
- System.Single
- System.TimeSpan
- System.UInt16
- System.UInt32
- System.UInt64
- System.Windows.Int32Rect
- System.Windows.Point
- System.Windows.Rect
- System.Windows.Size
- System.Windows.Vector
- System.Net.NetworkInformation.PhysicalAddress
This leaves me with only one question?
Where’s my Interface?
I can’t do a test for IParseable on the object, I can’t even search for the parse method by name and parameters because each object implements them differently. I could go with only the string parameter which they all implement, but I’ve learned from Civ4 that culture is important, and besides I wouldn’t want to fail the Turkey test.
Of course there’s a reason for this seemingly lazy design – interfaces can’t be declared as static. (That’s a conversation for a different day.) Ok, so given that they can’t, where do we go from here? Lets pretend that we still wanted to use an Interface for these classes.
interface IParseable { }
So now we’ve got an Empty Interface. Perhaps this would be better as a custom attribute, and if so please explain to me how to accomplish this next part with said custom attribute.
We then assign our interface to the DataType and implement a number of Parse methods
class DataType : IParseable
{
public static DataType Parse(string s)
{
//blah blah, use the string to create a new DataType
return new DataType();
}
}
Then in the System.Convert class we’d implement some function like this:
public static T Parse<T>(string s) where T : IParseable
{
MethodInfo mInfo = typeof(T).GetMethod(“Parse”, BindingFlags.Public | BindingFlags.Static);
if (mInfo != null)
{
T obj = (T) mInfo.Invoke(null, new object[] { s });
return obj;
}
return default(T);
}
Other methods could accept CultureInfo, FormatProviders and other params. Someone have a better way of doing this?
February 7, 2008
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.