簡體   English   中英

難以比較兩個DateTime實例

[英]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更早? 而且,如果我不能信任的比較,我怎么能寫一個嚴格的測試,以檢查的價值FeedbackDateFeedbackDao構造?

我的假設是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM