Validation Application Block – Unit Test Validation Logic

The Enterprise Library Validation Application Block (VAB) is a great library for putting your validation in your business layer where it belongs rather than in the UI. It allows us to apply attributes to the properties of our business objects like this:

   1:  public class Person
   2:  {
   3:      [StringLengthValidator(1, 20, MessageTemplate="First Name must be between 1-20 characters.")]
   4:      public string FirstName { get; set; }
   6:      [StringLengthValidator(1, 20, MessageTemplate="Last Name must be between 1-20 characters.")]
   7:      public string LastName { get; set; }
   8:  }

But are you unit testing your validation code properly? Consider this test method:

   1:  [TestMethod]
   2:  public void Person_Validation_Test()
   3:  {
   4:      var person = new Person();
   5:      person.FirstName = "Bill";
   6:      person.LastName = "Gates";
   8:      // Verify person is valid
   9:      var validationResults = Validation.Validate(person);
  10:      Assert.IsTrue(validationResults.IsValid, "Person should be valid.");
  12:      // Now make first name invalid
  13:      person.FirstName = string.Empty;
  14:      validationResults = Validation.Validate(person);
  15:      Assert.IsFalse(validationResults.IsValid, "Validation failed. FirstName should have been invalid.");
  17:      // Now make last name invalid
  18:      person.FirstName = "Bill";
  19:      person.LastName = string.Empty;
  20:      validationResults = Validation.Validate(person);
  21:      Assert.IsFalse(validationResults.IsValid, "Validation failed. LastName should have been invalid.");
  22:  }

At first glance, this test method might look ok. It tests the person to make sure it’s valid; it then makes FirstName invalid and then tests it; it then makes LastName invalid (while setting FirstName back to normal) and then tests it, etc.  But actually, there is a long list of things that are *wrong* with that test method – do *not* unit test this way. 

First off, a good unit test should only test ONE thing at a time.  In other words, you should have a test method for the FirstName property, another for the LastName property, etc.  In fact, you could have a test method for FirstName being string.Empty, another test method proving that FirstName is invalid when it is over 20 characters, etc.  Additionally, trying to “reset” the FirstName property back to normal before testing the LastName validation is just asking for trouble.  A test method constructed in this way is going to get monolithic and ultimately you’re going to forget to “reset” properly.  Additionally, you want to strive for DRY (Don’t Repeat Yourself) in your unit tests and how many times do you want to “reset” various properties back to “normal”?

If that wasn’t enough, there is something even more harrowing with this test.  That is: after FirstName is set to string.Empty and validity checked for, how do we *know* that the FirstName property is the thing that is causing this object to be invalid?  Sure, it looks obvious from the method but it’s not explicit and it’s uncertain. What if setting the FirstName property actually made some composite validator invalid but the FirstName itself was perfectly valid?  What if, when we got to the LastName validation, we didn’t remember to properly “reset” the FirstName property and we’re asserting LastName is invalid where it was really the FirstName test that came before it that was making it invalid? There are just too many opportunities to make mistakes.  To simplify all of this, the unit tests can be re-written like this:

   1:  [TestMethod]
   2:  public void Person_FirstName_Is_Invalid()
   3:  {
   4:      var person = CreateValidPerson();
   5:      person.FirstName = string.Empty;
   6:      person.AssertWithKey("FirstName");
   7:  }
   9:  [TestMethod]
  10:  public void Person_LastName_Is_Invalid()
  11:  {
  12:      var person = CreateValidPerson();
  13:      person.LastName = string.Empty;
  14:      person.AssertWithKey("LastName");
  15:  }
  17:  public static Person CreateValidPerson()
  18:  {
  19:      var person = new Person();
  20:      person.FirstName = "Bill";
  21:      person.LastName = "Gates";
  23:      ValidationResults validationResults = Validation.Validate(person);
  24:      Assert.IsTrue(validationResults.IsValid, "Person should be valid.");
  25:      return person;
  26:  }

Notice that each test method is testing only one property (you can fill in additional test methods to ensure it’s invalid when over 20 characters, etc.).  Additionally, each test starts from a known, valid state by re-using the static CreateValidPerson() method (which, inside, Asserts that it is, in fact, valid).  Most importantly, for each test, it uses the AssertWithKey() method to ensure that the *reason* it is invalid is, in fact, the property we’re interested in.  This AssertWithKey() is a simple extension method:

   1:  public static void AssertWithKey<T>(this T target, string keyToCheck)
   2:  {
   3:      ValidationResults validationResults = Validation.Validate(target);
   4:      ValidationResult result = validationResults.FirstOrDefault(r => r.Key == keyToCheck);
   5:      Assert.IsNotNull(result, "Validation Failed. {0} should have been invalid.", keyToCheck);
   6:  }

Notice the use of the FirstOrDefault() extension method.  The ValidationResults returned from the Validate() method is a collection which we can inspect to ensure that the key (the key is the property name) is tied to the validation result we are looking for and expecting – thus, ensuring that the test is in fact invalid *because* of the specific property we’re testing.

The above code was written for MSTest but can be easily adapted to NUnit, xUnit, or your unit testing framework of choice.

posted on Tuesday, January 27, 2009 10:27 PM Print
# re: Validation Application Block – Unit Test Validation Logic
3/25/2009 1:07 AM
Nice article. One thing I would add is that you should set up your validator inside the unit test, and inject it into your business object.
# re: Validation Application Block – Unit Test Validation Logic
3/25/2009 8:55 AM
@Doug - The set up phase for these types of unit tests is setting up the data in the business objects - not the validators themselves. These types of unit tests presume that the validators themselves are already well tested (e.g., the VAB) and this is meant to test your validation business logic as it pertains specifically to attributes defined on your custom business objects.
# re: Validation Application Block – Unit Test Validation Logic
2/16/2010 3:58 PM
This will not work if you are using ObjectValidator and the object is invalid. A safer way to use in unit test is by Ruleset.

Post Comment

Title *
Name *
Comment *  

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers

Google My Blog

Tag Cloud