How can "today's date" be varied for unit testing purposes?
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 defaultsbaselineDate
toDateTime.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 toDateTime.Today
. When testing, derive aDateCalculatorForTesting
fromDateCalculator
and setbaslineDate
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 w开发者_开发知识库hat others think.
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: Unit Testing: DateTime.Now
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; }
}
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.
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.
精彩评论