Unit Testing ASP.NET MVC ModelBinders

For today's post, we focus on unit testing our ModelBinders from our paging example.

June 24th, 2015 • MVC •
0 (0 votes)
Unit Testing

Recently, a reader posted a comment requesting me to add a unit test for testing our Model Binder with QueryStrings.

The post they were referring to was the ASP.NET MVC ModelBinder: Use ModelBinders for QueryStrings.

Today, as a quick post, we'll make a unit test for our ModelBinder.

Mockin' it up!

For my unit tests, I use Rhino Mocks. I know some people like Moq or TypeMock better, but this is a preference of mine.

First, we start with a NameValueCollection of "queryStrings."

// Arrange
var queryStringCollection = new NameValueCollection
{
    {"Page""1"},
    {"Size""20"}
};

Next, we need to create our ModelBindingContext. This requires a NameValueCollectionValueProvider (wow...that's a mouthful) and a ModelMetaDataProvider.

The ValueProvider is easy. We simply pass in the queryStringCollection whereas the ModelMetadata needs the model type that we'll be using for our ModelBinder which is the PagingModel.

var valueProvider = new NameValueCollectionValueProvider(queryStringCollection, null);
var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(nulltypeof(PagingModel));
var bindingContext = new ModelBindingContext
{
    ModelName = "PagingModel",
    ValueProvider = valueProvider,
    ModelMetadata = modelMetadata
};

As you can see, we finally create the ModelBindingContext with all of our properties ready for use.

Mocking HttpContext

Now the fun part.

Most developers hate mocking up the HttpContext. Using RhinoMocks, I have no qualms or issues about mocking it up.

I just need 2 properties from the HttpContext: Request and QueryString.

var mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
var mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockRequest.Stub(e => e.QueryString).Return(queryStringCollection);
mockHttpContext.Stub(x => x.Request).Return(mockRequest); 

Simple, right?

Controlling the Controller

Next, we need a controller to pass into the BindModel. So let's create our FAQ controller and instantiate our PagingBinder ModelBinder.

var controller = new FaqController();
controller.ControllerContext = new ControllerContext(
    mockHttpContext, new RouteData(), controller);
var binder = new PagingBinder();

For the grand finale, we call the BindModel with the ControllerContext and BindingContext passed into method.

// Act
PagingModel result = (PagingModel)binder.BindModel(controller.ControllerContext, bindingContext);

Assert the Assert

In our Assert, we simply check the PageSize and PageIndex is 20 and 1, respectively.

Here is our complete unit test for our PagingBinder ModelBinder.

[TestMethod]
public void RetrieveThePagingDataFromQueryStringUsingModelBinder()
{
    // Arrange
    var queryStringCollection = new NameValueCollection
    {
        {"Page""1"},
        {"Size""20"}
    };
    var valueProvider = new NameValueCollectionValueProvider(queryStringCollection, null);
    var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(nulltypeof(PagingModel));
    var bindingContext = new ModelBindingContext
    {
        ModelName = "PagingModel",
        ValueProvider = valueProvider,
        ModelMetadata = modelMetadata
    };
    var mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
    var mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
    mockRequest.Stub(e => e.QueryString).Return(queryStringCollection);
    mockHttpContext.Stub(x => x.Request).Return(mockRequest); 
    
    var controller = new FaqController();
    controller.ControllerContext = new ControllerContext(
        mockHttpContext, new RouteData(), controller);
    var binder = new PagingBinder();
    // Act
    PagingModel result = (PagingModel)binder.BindModel(controller.ControllerContext, bindingContext);
    // Assert
    Assert.AreEqual(result.PageSize, 20);
    Assert.AreEqual(result.PageIndex, 1);
}

Conclusion

The unit test for model binders almost have the same pattern for each specific model binder. If you wanted to use Request.Form, you could easily add in the mockRequest.Forms and add your own data into a NameValueCollection as well.

Mocking up the HttpContext is not as hard as it was before. Thanks to RhinoMocks for that simple interface.

Also, I want to thank Janek for the comment.

Was this informative? Share it!

Looking to become a better developer?

Sign up to receive ReSharper Design Pattern Smart Templates, ASP.NET MVC Guidelines Checklist, and Newsletter Updates!

Picture of Jonathan Danylko

Jonathan Danylko is a freelance web architect and avid programmer who has been programming for over 20 years. He has developed various systems in numerous industries including e-commerce, biotechnology, real estate, health, insurance, and utility companies.

When asked what he likes to do in his spare time, he replies, "Programming."

comments powered by Disqus