[英]Difficulty comparing two DateTime instances
我正在用C#(.NET 4.5)編寫一個單元測試類。 在其中一個測試中,我正在構造一個FeedbackDao
類的實例后檢查各種屬性的值。 在建設中, FeedbackDate
財產FeedbackDao
設置為DateTime.Now
。
FeedbackDao feedbackDao = new FeedbackDao();
// a couple of lines go here then I set up this test:
Assert.IsTrue(feedbackDao.FeedbackDate.CompareTo(DateTime.Now) < 0);
我的假設是feedbackDao.FeedbackDate應該總是比DateTime.Now
返回的當前時間早一點,即使它只有一毫秒,我的IsTrue
測試應該總是通過,但有時它會通過,有時它會失敗。 當我添加這樣的消息時:
Assert.IsTrue(feedbackDao.FeedbackDate.CompareTo(DateTime.Now) < 0,
feedbackDao.FeedbackDate.CompareTo(DateTime.Now).ToString());
消息有時讀取-1(表示FeedbackDate
早於Now
),有時讀取0(表示DateTime實例相等)。
為什么FeedbackDate
並不總是比Now
更早? 而且,如果我不能信任的比較,我怎么能寫一個嚴格的測試,以檢查的價值FeedbackDate
時FeedbackDao
構造?
我的假設是feedbackDao.FeebackDate應該總是比DateTime.Now返回的當前時間早一點,即使它只是一毫秒。
什么讓你有那個想法? 這表明1000次通話至少需要1秒 ,這似乎不太可能。
添加到這一事實, DateTime.Now
僅有約10-15ms IIRC的實際粒度, 很多時候如果你調用DateTime.Now
快速連續兩次你會得到相同值的兩倍。
出於可測試性的目的 - 以及依賴關系的干凈表達 - 我喜歡使用“時鍾”接口( IClock
),它總是用於提取當前系統時間。 然后,您可以編寫一個虛假的實現來控制您認為合適的時間。
此外,這個斷言是有缺陷的:
Assert.IsTrue(feedbackDao.FeebackDate.CompareTo(DateTime.Now) < 0,
feedbackDao.FeebackDate.CompareTo(DateTime.Now).ToString());
它有缺陷,因為它評估DateTime.Now
兩次...所以它報告的值不一定與它檢查的值相同。 它會更好:
DateTime now = DateTime.Now;
Assert.IsTrue(feedbackDao.FeebackDate.CompareTo(now) < 0,
feedbackDao.FeebackDate.CompareTo(now).ToString());
甚至更好:
DateTime now = DateTime.Now;
DateTime feedbackDate = feedbackDao.FeebackDate;
Assert.IsTrue(now < feedbackDate,
feedbackDate + " should be earlier than " + now);
您的測試不是那么有用 ,您斷言該值小於DateTime.Now
但這並不意味着它已正確設置為預期值。 如果未初始化日期時間,則它將具有DateTime.MinValue
並且該值將始終通過測試 。
此測試與測試feedbackDao.FeebackDate.CompareTo(DateTime.Now) <= 0
一樣有效,因此您不會遇到促使您編寫此問題的問題。
您需要提取DateTime.Now
的依賴項或使用支持模擬DateTime.Now
的模擬框架並聲明該值已初始化為正確的值。 您可以檢查Microsoft Moles ,現在已重命名為VS2012中的Fakes,這是我知道的唯一免費的模擬框架(最新版本的一種,因為它附帶VS並且不知道它是否可用於快遞版本),這將讓你替換對DateTime.Now
的調用。
如果不采用模擬框架,您可以通過執行以下操作來改進測試:
var lowerBoundary = DateTime.Now;
var dao = new FeedbackDao();
var upperBoundary = DateTime.Now;
Assert.IsTrue(dao.Date >= lowerBoundary && dao.Date <= upperBoundary);
在單元測試時,我認為DateTime.Now
是一個外部依賴項,因此需要進行模擬。 我在過去測試涉及DateTime.Now
場景時所做的,我剛剛通過類的構造函數傳遞了一個Func<DateTime>
,這允許我在測試期間模擬DateTime.Now
。
我更喜歡Jon Skeet關於使用類似IClock
接口的東西來包裝DateTime
屬性的建議,因為上次我這樣做時,我覺得愚蠢地創建一個新的接口和類來包裝單個屬性。 如果您需要測試多個靜態DateTime
屬性,我絕對同意IClock
建議。
例如,
public class Foo
{
private readonly Func<DateTime> timeStampProvider;
public Foo(Func<DateTime> timeStampProvider)
{
this.timeStampProvider = timeStampProvider;
}
public Foo() : this(() => DateTime.Now)
{
}
public bool CompareDate(DateTime comparisonDate)
{
// Get my timestamp
return comparisonDate > timeStampProvider();
}
}
然后,在測試期間,
var testFoo = new Foo(() => new DateTime(1, 1, 2010));
我通常使用模擬數據來驗證我的邏輯。 我圍繞模擬數據改進了測試場景。 正如DBM所建議的那樣。 模擬數據是一組通常是靜態或可配置的已知數據。 通常的做法是使用包含所有測試數據的XML文件,並在需要時加載它們。 我可以在我們的項目中給你一個例子。
嘗試
Assert.IsTrue(feedbackDao.FeebackDate.CompareTo(DateTime.Now) < 1);
要么
Assert.IsTrue(feedbackDao.FeebackDate - DateTime.Now < someMarginOfError);
時間通常相當精細 - 通常為10毫秒IIRC。
根據您的系統, DateTime.Now
不會每毫秒更新或打勾,它只會定期更新。 通常為10毫秒左右。 看到這里: DateTime.Now多久更新一次? 或者是否有更精確的API來獲取當前時間?
DateTime.Now不是100%准確。 它增加了大約130毫秒(從每個蜱的個人經驗)。 所以,如果你的方法足夠快,日期將等於datetime.now並且不小,這很可能。
如果你想要一個100%准確的計時器,你應該使用StopWatch類。
Msdn鏈接到秒表
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.