Unit Testing Data Validation with MVC

Published on May 11, 2015 by Jamie Munro

Problem


You've added some data validation and you want to test it out in action. The data validation can be standard data annotations, custom data attributes, or data validation implemented via an IValidatableObject.

Solution


Unit testing data validation can be accomplished by creating a ValidationContext in conjunction with the Validator helper class. To demonstrate how this is accomplished, I will use the ValidationModel class I created in the Removing Data Validation from MVC Controllers. As you may recall this class contained an IValidatableObject that ensured if the boolean FixedQuantity was true, the MaxQuantity property must be null.


Discussion


To begin, a new unit test project is required. If you do not already have one in your project, right-click your solution and select Add -> New Project. On the left hand-side select Test. This will provide the option to create a new Unit Test project. Once selected, provide a meaningful and complete the project creation.

The ValidationContext class resides in the System.ComponentModel.DataAnnotations namespace; a reference to this class must be added to your unit testing project. Right-click the project and select Add -> Reference. Under assemblies, find the DataAnnotations and include in your project.

At this point everything is setup and ready to test. The following code example tests that a ValidationException is thrown when the ValidationModel class property FixedQuantity is true and MaxQuantity contains a value.


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RedirectWithModel.Models;
using System.ComponentModel.DataAnnotations;

namespace UnitTestProject1
{
[TestClass]
public class ValidationModelTests
{
[TestMethod]
[ExpectedException(typeof(ValidationException))]
public void FixedQuantityAndMaxQuantityIsInvalid()
{
var model = new ValidationModel {
FixedQuantity = true,
MaxQuantity = 1
};

var validationContext = new ValidationContext(model);

Validator.ValidateObject(model, validationContext);
}
}
}


The test method FixedQuantityAndMaxQuantityIsInvalid is attributed with the ExpectedException attribute looking for a ValidationException. The test method passes if the Validator.ValidateObject throws this exception; otherwise, the test will fail.

That's it, more tests can be created to validate other expected failure or success scenarios.

Before finishing this post, I'm not a big fan of completely trusting the ExpectedException attribute. If your model contains multiple validation rules, this test does not ensure the expected exception is thrown. The next code example updates the previous validation test to perform a try/catch of the ValidationException. Inside the catch, the exception message is then asserted with the expected value, thus allowing us to be more certain the correct exception is being thrown.


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RedirectWithModel.Models;
using System.ComponentModel.DataAnnotations;

namespace UnitTestProject1
{
[TestClass]
public class ValidationModelTests
{
[TestMethod]
public void FixedQuantityAndMaxQuantityIsInvalid()
{
var model = new ValidationModel
{
FixedQuantity = true,
MaxQuantity = 1
};

ExecuteValidation(model, "A max quantity should not be specified when FixedQuantity is true");
}

private void ExecuteValidation(object model, string exceptionMessage)
{
try
{
var contex = new ValidationContext(model);
Validator.ValidateObject(model, contex);
}
catch (ValidationException exc)
{
Assert.AreEqual(exceptionMessage, exc.Message);

return;
}

Assert.Fail("Exception of type {0} should be thrown.", typeof(ValidationException));
}
}
}


After the ValidationModel is instantiated a new private function ExecuteValidation is called with the model and the expected exception message. I've created the private function as this seems like a nice reusable function when we have more than a single test method to assert. At the end of the function, if the catch method is not called (meaning the expected exception was not thrown), the test is forcibly failed via the Assert.Fail function.

Happy unit testing!

Tags: validation | ASP.NET | mvc | Testing | unit testing | ivalidatableobject | validationcontext

Related Posts

blog comments powered by Disqus