You can unit test a validation attribute by creating an instance of it and then testing the two methods:
- IsValid()
- FormatErrorMessage().
In this article, I’ll show examples of unit testing these methods in a custom validation attribute and in a built-in validation attribute (i.e. [Range]).
Unit testing a custom validation attribute
Consider the following custom validation attribute that implements IsValid(). It checks if an input is an even integer:
using System.ComponentModel.DataAnnotations;
public class EvenIntegerAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value is int number && number % 2 == 0)
return true;
return false;
}
}
Code language: C# (cs)
There are four test cases here:
- When the input is null, return false. Note: The ‘is’ operator returns false when used on a null.
- When the input is not the expected type (int), return false.
- When the input is an odd integer, return false.
- When the input is an even integer, return true.
Here’s the parameterized unit test for these test cases:
[DataRow(null, false)]
[DataRow("0", false)]
[DataRow(1, false)]
[DataRow(2, true)]
[TestMethod()]
public void IsValidTest(object input, bool expectedIsValidResult)
{
//arrange
var evenIntegerAttribute = new EvenIntegerAttribute();
//act
var actualIsValidResult = evenIntegerAttribute.IsValid(input);
//assert
Assert.AreEqual(expectedIsValidResult, actualIsValidResult);
}
Code language: C# (cs)
Unit testing the FormatErrorMessage() method
Validation attributes can also have a FormatErrorMessage() for returning error messages. I’ll show two examples of unit testing this method.
Example 1 – Simple error message string
Let’s say your FormatErrorMessasge() method has an interpolated string that shows an error message containing the name of the property with an error, like this:
public override string FormatErrorMessage(string name)
{
return $"{name} must be an even integer";
}
Code language: C# (cs)
Here’s a unit test that verifies FormatErrorMessage() uses the name parameter in a hardcoded error message:
[TestMethod()]
public void FormatErrorMessageTest_HasPropertyNameAndSpecificErrorMessage()
{
//arrange
var evenIntegerAttribute = new EvenIntegerAttribute();
string name = "Test";
string expected = "Test must be an even integer";
//act
var errorMessage = evenIntegerAttribute.FormatErrorMessage(name);
//assert
Assert.AreEqual(expected, errorMessage);
}
Code language: C# (cs)
Example 2 – Complex error message with a format string
Validation attributes can have error messages with format strings. This allows them to display very helpful error messages. It also makes unit testing FormatErrorMessage() a little more complex.
Here’s an example of unit testing FormatErrorMessage() when it has a format string (note: this is testing the built-in [Range] attribute):
using System.ComponentModel.DataAnnotations;
[TestMethod]
public void TestRange_ErrorMessageUsesExpectedFormat()
{
//act
var rangeValidation = new RangeAttribute(minimum: 0, maximum: 10);
rangeValidation.ErrorMessage = "{0} is out of range ({1}-{2})";
string expected = "Test is out of range (0-10)";
//arrange
var formattedErrorMessage = rangeValidation.FormatErrorMessage("Test");
//assert
Assert.AreEqual(expected, formattedErrorMessage);
}
Code language: C# (cs)
This is the equivalent of using the [Range] attribute like this:
[Range(minimum: 0, maximum: 10, ErrorMessage = "{0} is out of range ({1}-{2})")]
public int Seats { get; set; }
Code language: C# (cs)
Unit testing a built-in validation attribute
You can test built-in validation attributes, such as [Range]. The reason you’d do this is to verify the way you’re specifying the attribute parameter will work as expected. Consider the following code that uses the [Range] attribute to validate that the input is within a given date range:
using System.ComponentModel.DataAnnotations;
[TestMethod]
public void TestRange_WhenDateWithinRange_ReturnsTrue()
{
//act
var rangeValidation = new RangeAttribute(typeof(DateTime), minimum: "2022-05-01", maximum: "2022-05-31");
DateTime input = new DateTime(year: 2022, month: 5, day: 22);
//arrange
var isValid = rangeValidation.IsValid(input);
//assert
Assert.IsTrue(isValid);
}
Code language: C# (cs)
Note: You have to add reference to System.ComponentModel.DataAnnotations to be able to test the built-in attributes.