Regular expression attribute Testing–DateTime validation problem
When developing simple validation logic in Asp.Net Mvc you can use the built in validators. One of them is the RegularExpression Validator. I had a simple scenario with a property of DateTime type called StartDate. Validation format (yyyy-mm-dd). It should be a simple task , but details are always messy.
I created a simple pattern and tested it with various examples in one of the Regex Editors.
DateTime regular expression problem
[RegularExpression(@"^(19|20)dd([- /.])(0[1-9]|1[012])2(0[1-9]|[12][0-9]|3[01])$")]
public DateTime StartDate { get; set; }
To check if this solution works , two unit tests were created. First implementation of tests used the Regex class , but then I found out that you can use Attribute classes inside your code. I changed tests and used the RegularExpressionAttribute class inside test. Those tests are better because , with Regex our , we are checking if regex pattern is correct. With Attribute class used inside the test , we are testing actual scenario that is happening inside our app .
Problematic Tests
Result - SUCCESS
[TestFixture]
class CalendarEventRegExpTests
{
private string regex = @"^(19|20)(dd)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$";
[Test]
public void Should_match()
{
var dateTime = "2011-10-1";
var attribute = new RegularExpressionAttribute(regex);
Assert.IsTrue(attribute.IsValid(dateTime));
}
Result - SUCCESS
[Test]
public void Should_not_match()
{
var dateTime = "01-10-2001";
var attribute = new RegularExpressionAttribute(regex);
Assert.IsFalse(attribute.IsValid(dateTime));
}
}
Yey green light, They passed so it’s working ! I tested the app and … validation was always incorrect . First thought , my pattern is incorrect. But , it is working inside RegEx Editor so I it has to be correct.
Whats Wrong
It seems that when DateTime object is passed to the RegExpAttribute , something weird is happening and validation fails. I have simulated this scenario with simple test.
Result - FAIL
[Test]
public void Should_match()
{
var dateTime = new DateTime(2011,11,10);
var attribute = new RegularExpressionAttribute(regex);
Assert.IsTrue(attribute.IsValid(dateTime));
}
Maybe it’s the problem with the type of the object. This test converts DateTime object to string fail.
Result - FAIL
[Test]
public void Should_match()
{
var dateTime = new DateTime(2011,10,10);
var attribute = new RegularExpressionAttribute(regex);
Assert.IsTrue(attribute.IsValid(dateTime.ToString()));
}
Then I realized that .ToString() , method by default creates string including the hh-mm-ss. In my scenario those parameters were initialized with zeros My simple regex pattern wont match this string. Correctly formatted string passes the Test.
Result - SUCCESS
[Test]
public void Should_match()
{
var dateTime = new DateTime(2011,10,10);
var attribute = new RegularExpressionAttribute(regex);
Assert.IsTrue(attribute.IsValid(dateTime.ToString("yyyy-MM-dd")));
}
It’s time to look inside the RegularExpressionAttribute. Let me “Reflect” or “DotPeek” that for you.
Here is the code inside the class.
public override bool IsValid(object value)
{
this.SetupRegex();
string input = Convert.ToString(value, (IFormatProvider) CultureInfo.CurrentCulture);
if (string.IsNullOrEmpty(input))
{
return true;
}
else
{
Match match = this.Regex.Match(input);
if (match.Success && match.Index == 0)
return match.Length == input.Length;
else
return false;
}
}
The Highlighted part is the problem. .IsValid() method uses default.ToString(). DateTime is parsed to the string with hh-mm-ss and that’s the root of the problem.
Conclusion
There is a simple solution to this problem. You just need to attach DisplayFormat.
Lessons Learned
- “green light” in test doesn’t mean that your code is working .
- create tests on “real” data
- try simulate environment and context as much as possible. To many assumptions and your test isn’t testing real scenario. In my case , I used the string inside test when my app used DateTime object.