How can I test Controller.ViewData.ModelState
? I would prefer to do it without any mock framework.

- 398,270
- 210
- 566
- 880

- 23,005
- 25
- 91
- 119
-
Be more specific. What are you trying to test it for? – skb Jan 01 '09 at 23:24
6 Answers
You don't have to use a Mock if you're using the Repository Pattern for your data, of course.
Some examples: http://www.singingeels.com/Articles/Test_Driven_Development_with_ASPNET_MVC.aspx
// Test for required "FirstName".
controller.ViewData.ModelState.Clear();
newCustomer = new Customer
{
FirstName = "",
LastName = "Smith",
Zip = "34275",
};
controller.Create(newCustomer);
// Make sure that our validation found the error!
Assert.IsTrue(controller.ViewData.ModelState.Count == 1,
"FirstName must be required.");

- 15,808
- 23
- 102
- 173

- 17,712
- 6
- 74
- 89
-
1A virtual modifier on Errors would be nice, alas there's an arrange tax, which looks a trifle heavy to me. – Ed Blackburn Feb 09 '12 at 11:45
-
IMHO Better solution is to use mvc conveyor. In this way you get more realistic behavior of your controller, you should deliver model validation to it's destiny - attribute validations. Below post is describing this (http://stackoverflow.com/a/5580363/572612). – Vladimir Shmidt Aug 03 '12 at 07:11
-
1the article linked by Scott, seems tobe broken, can it be found else where? – Brian H May 15 '20 at 00:33
//[Required]
//public string Name { get; set; }
//[Required]
//public string Description { get; set; }
ProductModelEdit model = new ProductModelEdit() ;
//Init ModelState
var modelBinder = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
() => model, model.GetType()),
ValueProvider=new NameValueCollectionValueProvider(
new NameValueCollection(), CultureInfo.InvariantCulture)
};
var binder=new DefaultModelBinder().BindModel(
new ControllerContext(),modelBinder );
ProductController.ModelState.Clear();
ProductController.ModelState.Merge(modelBinder.ModelState);
ViewResult result = (ViewResult)ProductController.CreateProduct(null,model);
Assert.IsTrue(result.ViewData.ModelState["Name"].Errors.Count > 0);
Assert.True(result.ViewData.ModelState["Description"].Errors.Count > 0);
Assert.True(!result.ViewData.ModelState.IsValid);
-
I really like this method. As you state, it is far better at testing a model properly that has been decorated with validation attributes. – stevethethread Feb 28 '13 at 15:01
-
Very handy. I think I generally agree with folks that say that you shouldn't be testing your model properties, as that's a part of the framework, but there are times when it's critical to confirm for all future time that your controller is catching critical errors. – Ken Smith Dec 12 '13 at 19:22
-
Trying to find equivalent code for Web API. If anyone knows, perhaps they could update here? – Kieren Johnstone Jun 06 '15 at 09:28
-
-
Liked your response. You can create an extension method `public static void BindModelToController
(this Controller controller, T model) { ... }` – Razvan Sep 12 '16 at 12:09
For testing Web API, use the Validate method on the controller:
var controller = new MyController();
controller.Configuration = new HttpConfiguration();
var model = new MyModel();
controller.Validate(model);
var result = controller.MyMethod(model);

- 16,545
- 7
- 52
- 56
Ran into this problem for .NetCore 2.1 Here's my solution:
Extension Method
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace MyExtension
{
public static void BindViewModel<T>(this Controller controller, T model)
{
if (model == null) return;
var context = new ValidationContext(model, null, null);
var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(model, context, results, true))
{
controller.ModelState.Clear();
foreach (ValidationResult result in results)
{
var key = result.MemberNames.FirstOrDefault() ?? "";
controller.ModelState.AddModelError(key, result.ErrorMessage);
}
}
}
}
View Model
public class MyViewModel
{
[Required]
public string Name { get; set; }
}
Unit Test
public async void MyUnitTest()
{
// helper method to create instance of the Controller
var controller = this.CreateController();
var model = new MyViewModel
{
Name = null
};
// here we call the extension method to validate the model
// and set the errors to the Controller's ModelState
controller.BindViewModel(model);
var result = await controller.ActionName(model);
Assert.NotNull(result);
var viewResult = Assert.IsType<BadRequestObjectResult>(result);
}

- 707
- 8
- 26
-
1THIS! Why doesn't DNC2 work like this out of the box :( All I wanted was to make a unit test making sure that `If !ModelState.IsValid, return BadRequest()` in a controller action – lozzajp Jun 03 '18 at 10:49
-
1Works great unless the model is an IEnumerable. I hacked your code check if the model is an IEnumerable then to loop through each model doing the TryValidationObject check. – mobese46 Jun 22 '18 at 18:47
-
This not only let's you check that the error exists but also checks that it has the exact same error message as expected. For example both of these parameters are Required so their error message shows as "Required".
Model markup:
//[Required]
//public string Name { get; set; }
//[Required]
//public string Description { get; set; }
Unit test code:
ProductModelEdit model = new ProductModelEdit() ;
//Init ModelState
var modelBinder = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
() => model, model.GetType()),
ValueProvider=new NameValueCollectionValueProvider(
new NameValueCollection(), CultureInfo.InvariantCulture)
};
var binder=new DefaultModelBinder().BindModel(
new ControllerContext(),modelBinder );
ProductController.ModelState.Clear();
ProductController.ModelState.Merge(modelBinder.ModelState);
ViewResult result = (ViewResult)ProductController.CreateProduct(null,model);
Assert.IsTrue(!result.ViewData.ModelState.IsValid);
//Make sure Name has correct errors
Assert.IsTrue(result.ViewData.ModelState["Name"].Errors.Count > 0);
Assert.AreEqual(result.ViewData.ModelState["Name"].Errors[0].ErrorMessage, "Required");
//Make sure Description has correct errors
Assert.IsTrue(result.ViewData.ModelState["Description"].Errors.Count > 0);
Assert.AreEqual(result.ViewData.ModelState["Description"].Errors[0].ErrorMessage, "Required");

- 140
- 2
- 9
Adding to the great answers above, check out this fantastic use of the protected TryValidateModel method within the Controller class.
Simply create a test class inheriting from controller and pass your model to the TryValidateModel method. Here's the link: http://blog.icanmakethiswork.io/2013/03/unit-testing-modelstate.html
Full credit goes to John Reilly and Marc Talary for this solution.

- 3,017
- 1
- 36
- 41
-
2Please include the solution here rather than linking off to a blog post – csharpsql Mar 30 '17 at 09:31
-
4