.NET has very nice entity validation in the System.ComponentModel.DataAnnotations namespace.  I often use Required, RegularExpression and MaxLength attributes (among others).  For the web developer, they work nicely on the client and the server without much work on your part.  However, this article isn’t focused on the glories of .NET entity validation but on how to unit test it.
At the end of the article, you should be able to unit test your .NET entity validation with ease.

The Entity

Of course to start we need an entity so I’ll use the following Car entity:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace EntityValidationUnitTest
{
    public class Car : IValidatableObject
    {
        public int? Year { get; set; }

        [Required(ErrorMessage=Constants.MakeRequired)]
        public string Make { get; set; }

        [Required(ErrorMessage=Constants.ModelRequired)]
        public string Model { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Year.HasValue && Year > DateTime.Today.Year)
            {
                yield return new ValidationResult(Constants.YearInvalid, new List<string> { "Year" });
            }
        }
    }
}

Notes about the preceding code:

  1. Notice it implements IValidatableObject.  This allows us to do more complex validations in code.  The resultant errors still show up in our unit tests.
  2. I’m using an error message Constants class so that it is easy to compare in unit tests
  3. For an example of an entity that is auto-generated, see “One Last Thing” below

The Constants

Here are the constants:

public static class Constants
{
    public const string MakeRequired = "Make is required.";
    public const string ModelRequired = "Model is required.";
    public const string YearInvalid = "Year cannot be in the future.";
}

The Unit Tests

I’ll be using Visual Studio 2013 unit testing for these examples but they are just as valid using NUnit or any other test framework.

Valid Car Entity Test

The following test passes validation:

[TestMethod]
public void Validate_Car_Valid_Test()
{
    // Assemble
    var car = new Car
        {
            Year = 2000,
            Make = "Lexus",
            Model = "ES330"
        };

    // Act
    var validationResults = new List<ValidationResult>();
    var actual = Validator.TryValidateObject(car, new ValidationContext(car), validationResults, true);

    // Assert
    Assert.IsTrue(actual, "Expected validation to succeed.");
    Assert.AreEqual(0, validationResults.Count, "Unexpected number of validation errors.");
}

Notes about the preceding code:

  1. The name of the test tells me the code validates the car entity and that the result is valid.  This can be important with a long list of tests.
  2. I create a list of ValidationResult objects to pass into the TryValidateObject method so that it can fill it with errors.
  3. The Validator.TryValidateObject method is called to do the validation.  If it succeeds, the ‘actual’ variable will be ‘true’.  Otherwise, it will be false.

Make Required Test

The following tests that a null Make property validates correctly:

[TestMethod]
public void Validate_Car_MakeRequired_Test()
{
    // Assemble
    var car = new Car
        {
            Year = 2000,
            Make = null,
            Model = "ES330"
        };

    // Act
    var validationResults = new List<ValidationResult>();
    var actual = Validator.TryValidateObject(car, new ValidationContext(car), validationResults, true);

    // Assert
    Assert.IsFalse(actual, "Expected validation to fail.");
    Assert.AreEqual<int>(1, validationResults.Count, "Unexpected number of validation errors.");
    var msg = validationResults[0];
    Assert.AreEqual<string>(Constants.MakeRequired, msg.ErrorMessage);
    Assert.AreEqual<int>(1, msg.MemberNames.Count(), "Unexpected number of member names.");
    Assert.AreEqual<string>("Make", msg.MemberNames.ElementAt(0));
}

Notes on the preceding code:

  1. Notice the payoff on using error message constants here.
  2. The validationResults list should have been populated with one error message

Model Required Test

The following tests that a null Model property validates correctly:

[TestMethod]
public void Validate_Car_ModelRequired_Test()
{
    // Assemble
    var car = new Car
        {
            Year = 2000,
            Make = "Lexus",
            Model = null
        };

    // Act
    var validationResults = new List<ValidationResult>();
    var actual = Validator.TryValidateObject(car, new ValidationContext(car), validationResults, true);

    // Assert
    Assert.IsFalse(actual, "Expected validation to fail.");
    Assert.AreEqual<int>(1, validationResults.Count, "Unexpected number of validation errors.");
    var msg = validationResults[0];
    Assert.AreEqual<string>(Constants.ModelRequired, msg.ErrorMessage);
    Assert.AreEqual<int>(1, msg.MemberNames.Count(), "Unexpected number of member names.");
    Assert.AreEqual<string>("Model", msg.MemberNames.ElementAt(0));
}

 Year Invalid Test

The following tests that an invalid year validates properly:

[TestMethod]
public void Validate_Car_YearInvalid_Test()
{
    // Assemble
    var car = new Car
        {
            Year = DateTime.Today.Year + 1,
            Make = "Lexus",
            Model = "ES330"
        };

    // Act
    var validationResults = new List<ValidationResult>();
    var actual = Validator.TryValidateObject(car, new ValidationContext(car), validationResults, true);

    // Assert
    Assert.IsFalse(actual, "Expected validation to fail.");
    Assert.AreEqual<int>(1, validationResults.Count, "Unexpected number of validation errors.");
    var msg = validationResults[0];
    Assert.AreEqual<string>(Constants.YearInvalid, msg.ErrorMessage);
    Assert.AreEqual<int>(1, msg.MemberNames.Count(), "Unexpected number of member names.");
    Assert.AreEqual<string>("Year", msg.MemberNames.ElementAt(0));
}

Notes on the preceding code:

  1. Remember that the Year property was validated in the Validate method of the IValidatableObject interface.

One Last Thing

This technique will also work in the likely scenario where the entities are generated by Entity Framework or LINQ to SQL, etc.  Of course you cannot use the Required attribute in the generated class but you can use metadata to define the validation.

This is how to do it:

[MetadataType(typeof(CarMetadata))]
public partial class CarDb : IValidatableObject
{
    public int? Year { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Year.HasValue && Year > DateTime.Today.Year)
        {
            yield return new ValidationResult(Constants.YearInvalid, new List<string> { "Year" });
        }
    }
}

public class CarMetadata
{
    public int? Year { get; set; }

    [Required(ErrorMessage = Constants.MakeRequired)]
    public string Make { get; set; }

    [Required(ErrorMessage = Constants.ModelRequired)]
    public string Model { get; set; }
}

Notes on the preceding code:

  1. CarDb is a partial class that corresponds to the generated class.
  2. MetadataType points to the metadata class that you define.
  3. The CarDb partial class can still implement the IValidatableObject interface

For this to work, you must follow the steps outlined in this excellent post: http://www.roelvanlisdonk.nl/?p=2174

Conclusion

.NET Entity validation is a powerful tool for the developer and as it turns out, can be easily unit tested.