2

I am trying to mock my controller to test if the model state is invalid and the unit test basically return a 400 error if the model state is invalid. But the model state is validated in the BindModel() method of the Model Binder. Through unit test I am unable to hit the BindModel() method.

I have a generic method which mocks the Controller methods shown below:

public static async Task<HttpResponseMessage> MockController<T, 
TReturn>(
       this T controller, Func<T, Task<TReturn>> func, string requestUri = 
"http://tempuri.org", HttpMethod httpMethod = null) where T : ApiController
        {
          using (var request = new HttpRequestMessage(httpMethod ?? HttpMethod.Get, new Uri(requestUri)))
            {
                controller.Request = request;

                var actionResult = await func.Invoke(controller);
                // Create the response from the action result
                if (typeof(IHttpActionResult).IsAssignableFrom(typeof(TReturn)))
                {
                    return await ((IHttpActionResult)actionResult).ExecuteAsync(CancellationToken.None);
                }
                else
                {
                    return await Task.FromResult(request.CreateResponse(actionResult));
                }
            }
        }

The above method is used as shown below:
param is invalid

param=new someclass
{
    Data1=new List{"abc"}
};
var response = await controller.MockController(async c =>
{
    actionResult = await c.PostData(param);
    return actionResult;
});
Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);

What changes should I make in MockController method to call BindModel Method of the controller class being passed?

Model Binder

        public class MyModelBinderProvider : ModelBinderProvider
        {
            public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
            {
                return new MyModelBinder();
            }
        }

        public class MyModelBinder : IModelBinder
        {
            public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
            {
                var requestJson = actionContext.Request.Content.ReadAsStringAsync().Result;
                var domain = actionContext.ActionArguments["domain"].ToString();

                try
                {
                    var model = Configuration.Load(requestJson);
                    bindingContext.Model = model;
                    domain = Map.GetEntityName(domain.Replace('-', '_'));

                    var invalidReqFieldsDict = RequestValidator.Validate(domain, model);
                    if (invalidReqFieldsDict.Any())
                    {
                        var exceptionMessage = String.Join("", invalidReqFieldsDict);
                        bindingContext.ModelState.AddModelError(bindingContext.ModelName, exceptionMessage);
                        return false;
                    }
                    return true;
                }
                catch (JsonException e)
                {
                    bindingContext.ModelState.AddModelError(bindingContext.ModelName, e.Message);
                    return false;
                }
            }
        }

Any help is greatly appreciated! Thanks

  • 1
    You should write a unit test against the ModelBinder directly. That is simpler than testing 'via' the controller. – mjwills Jun 19 '17 at 10:36
  • [This question](https://stackoverflow.com/questions/5038430/how-to-unit-test-modelbinder-with-modelmetadata) shows how to unt test `BindModel()`, is that all you need? – stuartd Jun 19 '17 at 10:36
  • @stuartd: The controller method returns BadRequest for invalid model which is what I need to test but through the controller. – Saurabh Prasad Jun 19 '17 at 10:52
  • Can you show us the source code for your model binder? – mjwills Jun 19 '17 at 11:04
  • @mjwills: Have added the model binder – Saurabh Prasad Jun 19 '17 at 12:07
  • 1
    It's a lot easier if you simply move your logic into a logic layer. MVC is a presentation pattern and should only contain presentation/routing logic. That's one reason why it's so hard to test. If something is difficult to test you should take this as a clue that it's not working correctly, typically this means it's not abiding to the single responsibility principle. You should also be asking yourself, what am I testing here and why? If your testing MVC/HTTP/binding your wasting your time, test only logic – Liam Jun 19 '17 at 12:08

0 Answers0