Check out the Latest Articles:
BDD and Mspec… Meet Custom ModelBinder – Part 1

My goal today was to get to know the MVC2 ModelBinder, how it works, how it doesn’t, and where it likes to go for good Mexican food. Mmmmm. Mexican food… 

Focus. 

Well, I don’t presume to know the ModelBinder as well as I set out to, but give us time. For now, I thought I’d share a bit about writing a custom ModelBinder from the perspective of BDD. From here on out, I’m assuming you are up to speed with Visual Studio, ASP.NET MVC 2.0, C#, and the normal line-up. 

To help me follow BDD principles when programming, I like to use a testing framework like Machine.Specifications (mspec for short). To learn more about mspec, check out this list of links

To proclaim our first behavior, let’s start with a spec stub: 

public class when_binding_some_guestbook_post_data_to_the_viewmodel
{
    It should_not_be_null;
}

One thing I love about mspec is the code reads very much like English. It says, “When binding some guestbook post data to the viewmodel, it should not be null.” It’s a little developerish, but it’s synonymous to saying, “When you swim in your pool with floaties, you should not drown.” The “It” in this case will end up being the ViewModel, once we start building out the rest of the spec. What I want to do next is establish a context under which our spec will be operating. Since I’m writing the spec first, my variables and types won’t exist yet. So, without the “crutch” of Intellisense… 

public class when_binding_guestbook_post_data_to_viewmodel
{
    static TestModelBinder _modelBinder;
   static ModelBindingContext _bindingContext;�
   Establish context = () =>
   {
      var nameValueCollection = new NameValueCollection
      {
         { "Name", "Scott Hanselman" },
         { "Phone", "776-555-1212" }
      };
      _bindingContext = new ModelBindingContext
      {
         ModelName = "GuestBookViewModel",
         ValueProvider = new FormCollection(nameValueCollection)
      };
      _modelBinder = new TestModelBinder();
   };
   It should_not_be_null;
}

ReSharper makes life a lot easier when doing that kind of stuff. I was able to let ReSharper create my static fields without having to type them in manually. You can certainly do TDD/BDD without a tool like ReSharper. You could also re-invent the wheel, I suppose. To each his own, you know? 

To simulate form data, I created a NameValueCollection and populated it with a couple properties I plan on having in the ViewModel. Since I want to test a ModelBinder, I have to have a ModelBinderContext. All we need for this object is the future name of our ViewModel and our collection of values. In my case, I’m testing form values, so I convert my NameValueCollection into a FormCollection. 

Quick Side-Bar: ValueProvider is a funny little thing. There are probably other uses of the class, but in our world right this moment, ValueProvider is a property of our ModelBinderContext. There’s a ton of copy on the web that uses the old MVC1 implementation of the ValueProvider. The MVC team introduced some breaking changes to the ValueProvider in MVC2, so be aware as you’re exploring and using this pesky property. In short, .ValueProvider now implements IValueProvider instead of ValueProviderDictionary (now obsolete). 

Since I’m starting with specs and letting the rest of my code flow out from them, I end up typing in non-existent properties and types. In the sample above, I promised a TestModelBinder type. Resharper came in handy again to help me create that type. My TestModelBinder will implement IModelBinder, which is where the .BindModel() method came from.

public class CustomModelBinder : IModelBinder
{
   public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext)
   {
      throw new NotImplementedException();
   }
}

Alrighty. At this point, we haven’t told the spec to actually do anything yet. All we’ve said to this point is, “Here’s your swimming pool. You should not drown.” We haven’t said, “swim,” yet. Let’s rectify that.

public class when_binding_guestbook_post_data_to_viewmodel
{
   static TestModelBinder _modelBinder;
   static ModelBindingContext _bindingContext;
   static object _result;
   Establish context = () =>
   {
      var nameValueCollection = new NameValueCollection
      {
         { "Name", "Scott Hanselman" },
         { "Phone", "776-555-1212" }
      };
      _bindingContext = new ModelBindingContext
      {
         ModelName = "GuestBookViewModel",
         ValueProvider = new FormCollection(nameValueCollection)
      };
      _modelBinder = new TestModelBinder();
   };
   Because of = () => _result = _modelBinder.BindModel(null, _bindingContext);
   It should_not_be_null;
}

So, now, we have behavior! By adding “Because” to our spec, we’ve given our spec something to do with itself. Again, ReSharper helped create the _results field (an object because we don’t know the type yet). The .BindModel() method on our ModelBinder takes two parameters: ControllerContext and ModelBindingContext. Why pass in a null for the ControllerContext? Well, I am only trying to test the ModelBinder at this point. I don’t care about the Controller yet. If I can keep from working on that part at this point, I will.

Now, let’s finish the spec by testing our results.

public class when_binding_guestbook_post_data_to_viewmodel
{
   static TestModelBinder _modelBinder;
   static ModelBindingContext _bindingContext;
   static object _result;
   Establish context = () =>
   {
      var nameValueCollection = new NameValueCollection
      {
         { "Name", "Scott Hanselman" },
         { "Phone", "776-555-1212" }
      };
      _bindingContext = new ModelBindingContext
      {
         ModelName = "GuestBookViewModel",
         ValueProvider = new FormCollection(nameValueCollection)
      };
      _modelBinder = new TestModelBinder();
   };
   Because of = () => _result = _modelBinder.BindModel(null, _bindingContext);
   It should_not_be_null = () => _result.ShouldNotBeNull();
}

That’s it! With all that in place plus a stubbed out TestModelBinder() class, our spec should be fully prepared to FAIL! (uhh, that’s what we want to happen).

In my next post, I demonstrate how to walk through the BDD process and make tests pass.