Do your MVC model properties contain attributes?  Have you ever wanted to unit test the properties to verify that the ModelState fails or succeeds based on given values?  Below is a static method that can be used for your unit tests as you test your models.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyApp.Utilities.Testing
{
    public static class Models
    {
        public static List<ValidationResult> GetValidationErrors<T>(T model, out bool passed)
        {
            var validationContext = new ValidationContext(model, null, null);
            var validationResults = new List<ValidationResult>();
            passed = Validator.TryValidateObject(model, validationContext, validationResults, true);
            return validationResults;
        }
    }
}

In this code, the static method takes a model of type T and returns two objects: 1) an out boolean object (i.e. passed); and, 2) a list of validation results.  The boolean object returns whether the validation passed.  The list of validation results contain a list of validation errors if the model did not pass.

On line 14, we instantiate a ValidationContext passing in the model, null for an IServiceProvider, and null for a dictionary of items.  All we are interested in for the context is the model, itself.  Line 15, creates the list in which we will hold any errors with our model.

On line 16, we use the Validator.TryValidateObject static method of the DataAnnotations namespace to actually validate the model.  For the method, we pass in the model under test, the validation context we created on line 14, and the list we created on line 15.  Finally, we pass in true to validate all properties on the method.

Look at the code below to see how to use the utility method above.

Model under test:

using System;
using System.ComponentModel.DataAnnotations;

namespace MyApp.Models
{
    public class Person
    {
        [Required("First name is required.")]
        [StringLength(75, "First name must be less than 75 characters.")]
        public string FirstName { get; set; }

        [Required("Last name is required.")]
        [StringLength(75, "Last name must be less than 75 characters.")]
        public string LastName { get; set; }

        [Required("Age is required.")]
        [Range(18, 65, "Age must be between 18 and 65.")]
        public short Age { get; set; }
    }
}

Finally, the unit test:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Linq;
using MyApp.Models;
using t = MyApp.Utilities.Testing;

namespace MyApp.Tests.Models
{
    [TestClass]
    public class PersonTests
    {
        [TestMethod]
        public void Person_InvalidAge_ReturnsFalse()
        {
            // Arrange
            Person person = new Person() { FirstName = "Joshua", LastName = "Davis", Age = 15 };
            bool passed = false;

            // Act
            var errors = t.Models.GetValidationErrors<Person>(person, out passed);

            // Assert
            Assert.IsTrue(passed, errors[0].ErrorMessage);
        }
    }
}

Line 18, creates my sample Person object; and, line 19, the object to hold whether the validator passed.

Line 23 uses our utility method above to validate our Person model object.  If any validation errors are found, false is passed out and the errors are stored in the errors list.

Line 28 has our assertion and tests if passed is true.  If not, the first error message from our list of errors is displayed in the Test Explorer.

Additionally, you could test to see whether the first error message returned is equal to the expected validation error message for the Range attribute of the Age property.