views:

104

answers:

4

I use VS2008 targetting .NET 2.0 Framework, and, just in case, no I can't change this :)

I have a DateCalculator class. Its method GetNextExpirationDate attempts to determine the next expiration, internally using DateTime.Today as a baseline date.

As I was writing unit tests, I realized that I wanted to test GetNextExpirationDate for different 'today' dates.

What's the best way to do this? Here are some alternatives I've considered:

  • Expose a property/overloaded method with argument baselineDate and only use it from the unit test. In actual client code, disregard the property/overloaded method in favour of the method that defaults baselineDate to DateTime.Today. I'm reluctant to do this as it makes the public interface of the DateCalculator class awkward.
  • Create a protected field called baselineDate that is internally set to DateTime.Today. When testing, derive a DateCalculatorForTesting from DateCalculator and set baslineDate via the constructor. It keeps the public interface clean, but still isn't great - baselineDate was made protected and a derived class is required, both solely for testing.
  • Use extension methods. I tried this after adding the ExtensionAttribute, then realized it wouldn't work because extension methods can't access private/protected variables. I initially thought this was really quite an elegant solution. :(

I'd be interested in hearing what others think.

+4  A: 

You can use an interface that supplies the baseline date. Normally you would use an implementation that returns DateTime.Today but for testing purposes have one that lets your unit tests supply a date.

This could also be useful if it becomes necessary to use the current date on a datebase server or some other machine that is not necessarily where your code is running.

In fact, here's the same question with some more detailed answers than mine: http://stackoverflow.com/questions/2425721/unit-testing-datetime-now

Andrew Kennan
+1  A: 

One option is to create an internal overload which takes a DateTime, and delegate the real implementation to that:

public DateTime GetNextExpirationDate()
{
  return GetNextExpirationDate(DateTime.Today);
}

internal DateTime GetNextExpirationDate(DateTime after)
{
  // implementation goes here
}

You can then use InternalsVisibleToAttribute to make the overload visible to your test assembly, and your test assembly can then call it with DateTime values of its own choosing.

itowlson
A: 

You can change the operating system's time-of-day clock on the computer that's doing the testing. This is easy, and transparent, but watch out for problems with file timestamps.

Scott Smith
+1  A: 

I usually collect calls to the OS inside an abstraction/interface so that I can easily test it.. Just as Andrew as mentioned above.

However given only the need mentioned in the question; I feel 'Subclass and Override' is the simplest and least invasive way to get this done - Option 2 that the OP mentioned.

public class DateCalculator
{
  public DateTime GetNextExpirationDate() {  // call to GetBaseLineDate() to determine result }
  virtual protected GetBaseLineDate() {  return DateTime.Today; }
}

// in your test assembly
public class DateCalcWithSettableBaseLine : DateCalculator
{
  public DateTime BaseLine { get; set;}
  override protected GetBaseLineDate()
  {    return this.BaseLine;  }
}
Gishu