MVC2 Unit Testing, Populating ModelState

I love how testable ASP.NET MVC is, I also love MVC2’s model validation. However when trying to unit test a controller method that used the ModelState, I quickly learned that the ModelState is not populated when just newing up a Controller and calling one of its public methods. As usual, I think this is best narrated by example:

Example Model and Controller

public class PersonModel
{
[Required]
public string Name { get; set; }
}

public class PersonController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Register(Person person)
{
return View(new PersonModel());

}
[AcceptVerbs(HttpVerbs.Post)]

public ViewResult Register(Person person)

{
if (!ModelState.IsValid)

return View(model);

PersonService.Register(person);
return View("success");
}
}

Example of the Problem

[Test]

public void RegisterTest()

{
var model = new PersonModel { Name = String.Empty }; // This is model is invalid.
var controller = new PersonController();

var result = controller.Register(model);
// This fails because the ModelState was valid, although the passed in model was not.

Assert.AreNotEqual("success", result.ViewName);
}


Solution

Other solutions I have come across were adding the errors to the model state manually, or mocking the ControllerContext as to enable the Controller’s private ValidateModel method. I didn’t like the former because it felt like I wasn’t actually testing the model validation, and I didn’t like the latter because it seemed like a lot of work to both mocking things and then still have to manually expose a private method.

My solution is (I feel) pretty simple: Add an extension method to the ModelStateDictionary that allows you to pass in a model, and it will then validate that model and add it’s errors to the dictionary.

public static void AddValidationErrors(this ModelStateDictionary modelState, object model)
{
var context = new ValidationContext(model, null, null);

var results = new List<ValidationResult>();

Validator.TryValidateObject(model, context, results, true);
foreach (var result in results)

{

var name = result.MemberNames.First();

modelState.AddModelError(name, result.ErrorMessage);
}
}


Example of the Solution

[Test]

public void RegisterTest()

{
var model = new PersonModel { Name = String.Empty }; // This is model is invalid.
var controller = new PersonController();

// This populates the ModelState errors, causing the Register method to fail and the unit test to pass.
controller.ModelState.AddValidationErrors(model);
var result = controller.Register(model);
Assert.AreNotEqual("success", result.ViewName);

}

kick it on DotNetKicks.com