How to do Unit tests on MVC validation

Total Post:119

Points:835
 1384  View(s)
Ratings:
Rate this:

How can I test that my controller action is putting the correct errors in the ModelState when validating an entity, when I'm using DataAnnotation validation in MVC 2 Preview 1?

Some code to illustrate. First, the action:

    [HttpPost]
    public ActionResult Index(BlogPost b)
    {
        if(ModelState.IsValid)
        {
            _blogService.Insert(b);
            return(View("Success",b));
        }
        return View(b);
    }

And here's a failing unit test that I think should be passing but isn't (using MbUnit & Moq):

[Test]
public void When_processing_invalid_post_HomeControllerModelState_should_have_at_least_one_error()
{
    // arrange
    var mockRepository = new Mock<IBlogPostSVC>();
    var homeController = new HomeController(mockRepository.Object);
 
    // act
    var p = new BlogPost { Title = "test" };  // date and content should be required
    homeController.Index(p);
 
    // assert Assert.IsTrue(!homeController.ModelState.IsValid);
}

I guess in addition to this question, should I be testing validation, and should I be testing it in this way?

  1. Post:110

    Points:776
    Re: How to do Unit tests on MVC validation

    Instead of passing in a BlogPost you can also declare the actions parameter as FormCollection. Then you can create the BlogPost yourself and call UpdateModel(model, formCollection.ToValueProvider());. This will trigger the validation for any field in the FormCollection.

        [HttpPost]
        public ActionResult Index(FormCollection form)
        {
            var b = new BlogPost();
            TryUpdateModel(model, form.ToValueProvider());
     
            if (ModelState.IsValid)
            {
                _blogService.Insert(b);
                return (View("Success", b));
            }
            return View(b);
        }

    Just make sure your test adds a null value for every field in the views form that you want to leave empty.

    I found that doing it this way, at the expense of a few extra lines of code, makes my unit tests resemble the way the code gets called at runtime more closely making them more valuable. Also you can test what happens when someone enters "abc" in a control bound to an int property.

      Modified On Apr-07-2018 12:28:15 AM

Answer