When you encounter the problem of writing tests for ModelState.IsValid
for the first time it can be very frustrating, especially because ModelBinding doesn’t work in unit tests. This article details how to writeunit tests against a controller calling ModelState.IsValid
.
Let’s set the scene. You’re testing a controller that you’ve built – the AddController
. And you’re testing the Add
action for that controller. In the code there are two paths through the controller.
- If the ModelState is valid, the “Added” View is called
- If the ModelState is invalid, the “Add” View is called
The code looks like this:
public class AddController |
public ActionResult Add(ModelObject model) |
And the code for testing an invalid model:
public void InvalidModelObject_ShouldReturnAddView() |
var expectedViewName = "Add" ; |
var controller = new AddController(); |
var model = ModelObject.InvalidModelObject(); |
var result = controller.Add(model) as ViewResult; |
Assert.AreEqual(expectedViewName, result.ViewName); |
The above code attempts to pass a ModelObject
to the Add
action and cause the !ModelState.IsValid
to return false.
When running through a web page, this is done automatically withModel Binding. But running with unit tests you will quickly came across aproblem; running within a unit testing context, Model Binding doesn’t work! And believe it or not, that’s exactly how Model Binding should work because there’s a problem with this code.
Have you spotted it? This code isn’t testing the controller. It’stesting the Model Binding. And that’s a waste of time because Microsoftalready tested Model Binding before they shipped ASP.NET MVC!
Having ModelState.IsValid
equate to true or false is aside effect of Model Binding. However tests should be written so thatthey’re free from side effects. So relying on Model Binding to set ModelState.IsValid
as a side effect is wrong.
How do you test ModelState.IsValid?
So what’s the right way to test ModelState.IsValid
in unit tests? The correct way to test ModelState.IsValid
is by adding a ModelState error to the controller, before the action is called.
public void InvalidModelState_ShouldReturnAddView() |
var expectedViewName = "Add" ; |
var controller = new AddController(); |
controller.ModelState.AddModelError( "key" , "model is invalid" ); |
var model = ModelObject.InvalidModelObject(); |
var result = controller.Add(model) as ViewResult; |
Assert.AreEqual(expectedViewName, result.ViewName); |
By calling AddModelError
you’re setting ModelState.IsValid
to false, because the ModelState
has an error. This successfully exercises your test case and makes it clear for anyone reading the test what is being tested.