Unit testing custom model binder in ASP.NET MVC 2
Asked Answered
I

2

21

I've wrote custom model binder in project, that uses ASP.NET MVC 2. This model binder bind just 2 fields of model:

public class TaskFormBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext,
        ModelBindingContext bindingContext,
        PropertyDescriptor propertyDescriptor)
    {           
        if (propertyDescriptor.Name == "Type")
        {
            var value = bindingContext.ValueProvider.GetValue("Type");
            var typeId = value.ConvertTo(typeof(int));
            TaskType foundedType;
            using (var nhSession = Domain.GetSession())
            {
                foundedType = nhSession.Get<TaskType>(typeId);
            }
            if (foundedType != null)
            {
                SetProperty(controllerContext, bindingContext, propertyDescriptor, foundedType);
            }
            else
            {
                AddModelBindError(bindingContext, propertyDescriptor);
            }
            return;
        }
        if (propertyDescriptor.Name == "Priority")
        { /* Other field binding ... */
            return;
        }
        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
    }
}

How can i test this model binder using standart VS unit testing? Spent some hours googling, find couple examples (http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx), but this examples is for MVC1, and dont work when using MVC2.

I appreciate your help.

Infamy answered 2/1, 2010 at 19:50 Comment(0)
U
42

I've modified Hanselman's MVC 1 example to test ASP.Net MVC 2 model binders...

[Test]
public void Date_Can_Be_Pulled_Via_Provided_Month_Day_Year()
{
    // Arrange
    var formCollection = new NameValueCollection { 
        { "foo.month", "2" },
        { "foo.day", "12" },
        { "foo.year", "1964" }
    };

    var valueProvider = new NameValueCollectionValueProvider(formCollection, null);
    var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(FwpUser));

    var bindingContext = new ModelBindingContext
    {
        ModelName = "foo",
        ValueProvider = valueProvider,
        ModelMetadata = modelMetadata
    };

    DateAndTimeModelBinder b = new DateAndTimeModelBinder { Month = "month", Day = "day", Year = "year" };
    ControllerContext controllerContext = new ControllerContext();

    // Act
    DateTime result = (DateTime)b.BindModel(controllerContext, bindingContext);

    // Assert
    Assert.AreEqual(DateTime.Parse("1964-02-12 12:00:00 am"), result);
}
Underskirt answered 22/2, 2010 at 12:59 Comment(3)
Your answer saved me from mucking around in the MVC source. I tried to do the same update and came up with almost the same result. Unfortunatly, I wasn't setting the ModelMetaData on the ModelBindingContext. Without it, you get a nondescript NullReferenceException on BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext).Microhenry
I was about to create a custom ValueProvider, but thanks to the NameValueCollectionValueProvider that you've mentioned. This was useful. Thanks.Harbaugh
Something worth pointing out here for others like me :) The ModelName being assigned to ModelBindingContext is important, and cannot simply be an arbitrary name. I use string.Empty to avoid any confusion.Separation
A
1

The general approach is to create a mock ControllerContext, mock ModelBindingContext, and mock PropertyDescriptor, and then call the method.

If your model binder uses other services, which it looks like yours does (you're using NHibernate?), then you'll have to abstract those out and provide mocks of them as well.

The unit test code will look something like this:

// Arrange
ControllerContext mockControllerContext = ...;
ModelBindingContext mockModelBindingContext = ...;
PropertyDescriptor mockPropertyDescriptor = ...;
SomeService mockService = ...;

TaskFormBinder taskFormBinder = new TaskFormBinder();
taskFormBinder.Service = mockService;

// Act
taskFormBinder.BindProperty(
    mockControllerContext, mockModelBindingContext, mockPropertyDescriptor);

// Assert
// ... here you need to inspect the values in the model binding context to see that it set the right properties

What problem(s) are you having writing the unit test?

Asinine answered 2/1, 2010 at 21:46 Comment(2)
Just need an example of mocking ControllerContext, ModelBindingContext and PropertyDescriptor. Yes, Im using NHibernate, but I know how abstract and mock this layer.Infamy
One problem with mocking ModelBindingContext is that its most important property (ModelType) is non-virtual, meaning that inheritance-based frameworks (e.g., Moq) can't mock its behavior.Calandracalandria

© 2022 - 2024 — McMap. All rights reserved.