Sunday 28 September 2008

Testing DateTime Type Properties for Value Equality in Integration Tests

I've just been writing some integration tests to ensure that some Repositories I'm writing work with NHibernate properly, basically ensuring that the mappings do what I want. The Repository classes in question use the latest release of NHibernate (2.0.0.GA) for their implementation and I'm using MbUnit for my integration tests.

To try and save on the number of asserts that I have to write I thought that I'd try to use the Assert.AreValueEqual method that MbUnit provides. This allows me to write this in my test:

foreach (PropertyInfo propertyInfo in typeof(Candidate).GetProperties())
{
Assert.AreValueEqual(propertyInfo,expected,actual, null);
}

The context for this test is that I'm persisting a copy of a new Candidate object to the database (using NHibernate, but not my repository) which exposes a property, RegistrationDate, of the type ?DateTime. When this property is cycled through to the Assert always fails. Initially this puzzled me as the two DateTime instances appeared to be the same. A quick look at the DateTime.Equals(DateTime) method in reflector soon revealed that when this operation is called it is the InternalTicks property that is the subject of the comparison:

return (this.InternalTicks == value.InternalTicks);

When I get the DateTime back from the database this is not as precise as it was originally in the world of the CLR. The expected (original) value is 633581595292333682 whilst the actual (retrieved) value is 633581595290000000. This isn't enough of a difference to be significant for my purposes, as the ToString() representations are Equal and I don't need a level of accuracy beyond days, let alone fractions of a second.

I figure my options here are:

  1. Don't use Assert.AreValueEqual and hand write each Assertion as an Assert.AreEqual instead;
  2. Store the DateTime in the database as the value of the InternalTicks rather than the DateTime value itself. The obvious problem I can see with this is that any extraction, such as to a reporting database, would become more problematic if this was done. I figure there are times when testing should inform design, but this is clearly not one of them!
  3. Isolate NullableDateTime type properties and deal with these differently.

By changing the code a little I am able to isolate DateTime properties and deal with these using the ToString() method like this:

Type NullableDateTimeType = Type.GetType("System.Nullable`1[[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]");
foreach (PropertyInfo propertyInfo in typeof(Candidate).GetProperties())
{
if (propertyInfo.PropertyType == NullableDateTimeType)
{
DateTime expectedDateTime = (DateTime)propertyInfo.GetValue(expected, null);
DateTime actualDateTime = (DateTime)propertyInfo.GetValue(expected, null);
Assert.AreEqual(expectedDateTime.ToString(), actualDateTime.ToString(), "The expected RegistrationDate is not equal to the actual RegistrationDate.");
}
else
{
Assert.AreValueEqual(propertyInfo, expected, actual, null);
}
}

I'm not too sure how much coding I've saved myself with this, but I figure that as the number of properties involved increases it will reduce maintenance. If anyone out there has a better solution (doing this at gone 2 in the morning makes me think that there probably is a better solution) then please feel free to suggest it.

No comments: