How to Unit Test a custom ModelBinder using Moq?
Asked Answered
E

1

13

I'm having some difficulty writing some Unit Tests to test a custom ModelBinder that I created. The ModelBinder I'm trying to Unit Test is the JsonDictionaryModelBinder that I posted here.

The problem I'm having is getting the Mocking all setup using Moq. I keep getting Null Exceptions due to the HttpContextBase not being Mocked correctly. I think.

Could someone help me figure out what I'm not doing correclty?

Here's a sample of the Unit Test I'm trying to write that doesn't work:

[TestMethod()]
public void BindModelTest()
{
    JsonDictionaryModelBinder target = new JsonDictionaryModelBinder();

    NameValueCollection nameValueCollection = new NameValueCollection() {
        {"First", "1"},
        {"Second", "2"},
        {"Name", "Chris"},
        {"jsonValues", "{id: 200, name: 'Chris'}"}
    };

    HttpContextBase httpContext = MockHelper.FakeHttpContext(HttpVerbs.Post, nameValueCollection);

    ControllerContext controllerContext =
        new ControllerContext(new RequestContext(httpContext, new RouteData()), new Mock<Controller>().Object);


    Predicate<string> predicate = propertyName => (propertyName == "jsonValues");
    ModelBindingContext bindingContext = new ModelBindingContext()
    {
        Model = null,
        ModelType = typeof(JsonDictionary),
        ModelState = new ModelStateDictionary(),
        PropertyFilter = predicate,
        ValueProvider = new Dictionary<string, ValueProviderResult>() { { "foo", null } }
    };

    //object expected = null; // TODO: Initialize to an appropriate value
    var actual = target.BindModel(controllerContext, bindingContext) as JsonDictionary;

    Assert.IsNotNull(actual);

    Assert.AreEqual("Chris", actual["name"]);
    //Assert.AreEqual(expected, actual);
    Assert.Inconclusive("Verify the correctness of this test method.");
}

Here's the "FakeHttpContext" method used above:

public static class MockHelper
{
    public static HttpContextBase FakeHttpContext(HttpVerbs verbs, NameValueCollection nameValueCollection)
    {
        var httpContext = new Mock<HttpContextBase>();

        var request = new Mock<HttpRequestBase>();
        request.Setup(c => c.Form).Returns(nameValueCollection);
        request.Setup(c => c.QueryString).Returns(nameValueCollection);

        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        httpContext.Setup(c => c.Request).Returns(request.Object);

        var u = verbs.ToString().ToUpper();
        httpContext.Setup(c => c.Request.RequestType).Returns(
            verbs.ToString().ToUpper()
        );

        httpContext.Setup(c => c.Response).Returns(response.Object);
        httpContext.Setup(c => c.Server).Returns(server.Object);
        httpContext.Setup(c => c.User.Identity.Name).Returns("testclient");
        return httpContext.Object;
    }
}
Earnestineearnings answered 3/7, 2009 at 22:7 Comment(0)
C
7

The culprit is this line:

httpContext.Setup(c => c.Request.RequestType).Returns(
                verbs.ToString().ToUpper()
            );

This is technically a second Setup on the Request object, and it is wiping out the original Setup, even though you're going "past" it in the object hierarchy. I'm not sure if this is a bug in Moq or desired behaviour, I've run into this before as well and haven't gotten around to checking it out.

You can solve it by moving that line to where you're setting up your request above, and setting it up directly, rather than by going through the httpContext. So,

request.Setup(c => c.RequestType).Returns(verbs.ToString().ToUpper());

I also notice that the "var u" that you declare is not being used ;)

Compensation answered 4/7, 2009 at 2:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.