Unit Testing ASP.NET MVC ModelBinders

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

Written by Jonathan "JD" Danylko • Last Updated: • MVC •
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.

ASP.NET 8 Best Practices on Amazon

ASP.NET 8 Best Practices by Jonathan Danylko


Reviewed as a "comprehensive guide" and a "roadmap to excellence" with over 120 Best Practices for ASP.NET Core 8, Jonathan's first book by Packt Publishing explores proven techniques for every phase of the SDLC.

Learn industry-standard concepts to improve your coding, debugging, and deployment of ASP.NET Core websites.

Order now on Amazon.com button

Picture of Jonathan "JD" Danylko

Jonathan "JD" Danylko is an author, web architect, and entrepreneur who's been programming for over 30 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus