DateTimeAssert

Comparing date times in a unit test can be a challenging thing. Because the date time is most of the time an external dependency you don’t fully control, it can be desirable to allow some tolerance. Unfortunately, both the Microsoft Visual Studio Tools and the NUnit Framwork don’t support such a thing out of the box. Another issue can be that the fail messages use the ToString method of objects, and the default implementation of a date time does not reveal the milliseconds, which can be quiet annoying.

This implementation gives you both the option to specify a tolerance, as it shows the milliseconds of the expected and actual date time. It even shows the difference between the two if the tolerance is bigger than zero.

using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Text;

namespace Qowaiv.UnitTests.TestTools
{
	public static class DateTimeAssert
	{
		/// <summary>Verifies that two date times are equal. Two date times are
		/// considered equal if both are null, or have the same value.
		/// If they are not equal an NUnit.Framework.AssertionException is
		/// thrown.
		/// </summary>
		/// <param name="expected">
		/// The date time that is expected
		/// </param>
		/// <param name="actual">
		/// The actual date time
		/// </param>
		/// <param name="tolerance">
		/// The accepted tolerance between the actual and expected date time.
		/// </param>
		/// <param name="message">
		/// The message to display in case of failure.
		/// </param>
		/// <param name="args">
		/// Array of objects to be used in formatting the message
		/// </param>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// If the specified tolerance is negative.
		/// </exception>
		/// <exception cref="NUnit.Framework.AssertionException">
		/// If the assertion fails.
		/// </exception>
		[DebuggerStepThrough]
		public static void AreEqual(DateTime? expected, DateTime? actual, string message, params object[] args)
		{
			AreEqual(expected, actual, TimeSpan.Zero, message, args);
		}

		/// <summary>Verifies that two date times are equal. Two date times are
		/// considered equal if both are null, or the difference between the
		/// date times is smaller then the specified tolerance.
		/// If they are not equal an NUnit.Framework.AssertionException is
		/// thrown.
		/// </summary>
		/// <param name="expected">
		/// The date time that is expected
		/// </param>
		/// <param name="actual">
		/// The actual date time
		/// </param>
		/// <param name="tolerance">
		/// The accepted tolerance between the actual and expected date time.
		/// </param>
		/// <param name="message">
		/// The message to display in case of failure.
		/// </param>
		/// <param name="args">
		/// Array of objects to be used in formatting the message
		/// </param>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// If the specified tolerance is negative.
		/// </exception>
		/// <exception cref="NUnit.Framework.AssertionException">
		/// If the assertion fails.
		/// </exception>
		[DebuggerStepThrough]
		public static void AreEqual(DateTime? expected, DateTime? actual, TimeSpan tolerance, string message, params object[] args)
		{
			Guard.NotNegative(tolerance, "tolerance");

			if (actual.HasValue && expected.HasValue)
			{
				var difference = (actual.Value - expected.Value).Duration();

				if (difference > tolerance)
				{
					var sb = new StringBuilder();
					sb.AppendFormat("Expected:<{0:yyyy-MM-dd HH:mm:ss.FFFFFFF}>. ", actual);
					sb.AppendFormat("Actual:<{0:yyyy-MM-dd HH:mm:ss.FFFFFFF}>.", expected);
					if (tolerance > TimeSpan.Zero)
					{
						sb.AppendFormat(" Difference:<{0:#,##0.0######} seconds>.", difference.TotalSeconds);
					}
					if (!String.IsNullOrEmpty(message))
					{
						sb.Append(' ').AppendFormat(message, args);
					}
					Assert.Fail(sb.ToString());
				}
			}
			else
			{
				Assert.AreEqual(expected, actual, message, args);
			}
		}
	}
}

Leave a Reply