Nick Hodges

Fun With Testing DateUtils.pas #2

16 Mar

First of all, thanks for the help improving the CreateRandomDate function.  I confess that I didn’t spend enough time thinking on it, and I’ll also confess that you guys are way smarter than I am.  ;-)  I’ll post the “updated” version for your perusal in a separate post. 

In addition, you all were right – that code formatting sucks.   The plug-in I got for Live Writer was not very configurable.  I am now using John Kasters “YAPP” tool, and it all looks a lot better. 

Okay, back to the show…..

So to get going with unit testing DateUtils.pas, I naturally simply “plugged in” to our existing RTL unit test framework.  We have an existing RTL set of unit tests for running DUnit tests on the RTL.  I simply added the unit UnitTest.DateUtils.pas to the project, created the new class:

TDateUtilsTests = class(TTestCase)

and I was in business.  From there, it’s merely a matter of declaring published methods that run the DUnit tests.

So, I started right at the top with DateOf and TimeOf. So, what to test? Well, the most obvious thing: Does DateOf actually return the date portion of a given TDateTime?  Well, lets create a TDateTime with a random time, then, lets create a TDate with the same date but no time at all, and see if DateOf can do it’s magic?

procedure TDateUtilsTests.Test_DateOf;
  TestDate: TDateTime;
  Expected: TDateTime;
  Result  : TDateTime;
  TestDate := EncodeDateTime(1945, 12, 1, 1, 46, 13, 112);
  Expected := EncodeDate(1945, 12, 1);
  Result   := DateOf(TestDate);
  CheckTrue(SameDate(Result, Expected), 'Test date and Expected date were not the same.'
             + ' DateOf function failed. Test #1');

So this is a pretty straightforward test – you create two dates, and see if they are the same after the call to DateOf.  Simple.

What if you, say, increment the time part by one millisecond.  Come on, that can’t hurt anything right?  Better make sure:

  // This test will fail if it gets run at 23:59.999 at night.
  // I'm willing to gamble that this will never happen.
  TestDate := Now;
  Expected := IncMillisecond(TestDate);
  Result   := DateOf(TestDate);
  CheckTrue(SameDate(Result, Expected), 'Test date and Expected date were not the same.'
          + ' DateOf function failed. Test #2');

Okay, those are some “positive test cases”.  (I have a bunch more different ones along these lines….)  What about testing the negative case?  That is, test where we know that the two dates should be different after the call, and we test to make sure that they are, indeed different.

  TestDate := Now;
  Expected := DateOf(IncDay(TestDate));
  Result   := DateOf(TestDate);
  CheckFalse(SameDate(Result, Expected), 'Test date and Expected date should have'
	+ ' been different but they weren''t.  Test #2');

I have a similar set of tests for TimeOf.  These are pretty basic, but that is where you start, right?  With the basics?  From there, I wrote tests that change only the milliseconds, the seconds, the minutes and the hours.  All should never allow the DateOf function to return anything other than the date.  For TimeOf, I do the same – change the year, month, and date and make sure the time is the same.  Then I purposefully change the time and make sure that the function actually does change the time. 

Now, some of you are going to chastise me for using other DateUtils.pas functions to write tests.  Two schools of thought on that.  One says that you should never rely on anything outside of the actually call being tested.  Another says to use those library functions because they’ll get tested all the more when used in other tests. I’m going to be following the latter philosophy, and as well see in a later post, this way of doing things actually will reveal a pretty significant bug in a routine that was used to test another routine.

4 Responses to “Fun With Testing DateUtils.pas #2”

  1. 1
    Barrier Property Testing and Instrument Usage | Used Test Equipment Says:

    [...] Nick Hodges » Blog Archive » Fun W&#1110t&#1211 Testing DateUtils.pas #2 [...]

  2. 2
    Anders Isaksson Says:

    But what if DateOf(X) just returns X untouched, while SameDate(X, Y) just tests the date part of X and Y? Wouldn’t this test say "success" while we all know that DateOf() is wrong?

  3. 3
    Nick Hodges Says:

    Anders — good point, I’ll write a test to check for that.

    DateOf is supposed to return a date only without any time. If there is a time on it, then that is an error.

  4. 4
    John Herbster Says:

    Nick, I am glad you tackle DateUtils with DUnit. I Tried and gave it up several years ago. I expect that you will have more success.

    For testing date functions, I prefer exhaustive testing, because it is so fast to check all the dates from year 1 to year 9999. You only have to be careful that you are working with exact whole numbers.

    Because of what I call the hidden value problem inherent with encoding dates and times into a TDateTime, a.k.a. double, if one codes arithmetic on time values, one often does not get the expected result. For example (DateTimeA + DeltaTimeB) – DeltaTimeB will often not be DateTimeA.

    Exhaustive testing of functions on all possible time values is impossible.

    There are some ways around this: One is to convert all TDateTime values to the nearest Int64 millisecond before doing the checking. Another is to stick with using doubles and normalize with
    function NormalizeDateTime(const DT: double): double;
    const MSD = 24*60*60*1000;
    begin Result := round(DT*MSD)/MSD end;
    The writers of the TTimeStamp functions made this assumption.

    I have attached some test and fix code to a post with the subject “Tests of some DateUtils fixes” that I am dropping into e.p.attachments.

    Best regards, JohnH

© 2014 Nick Hodges | Entries (RSS) and Comments (RSS)

Your Index Web Directorywordpress logo