2017-07-28 112 views
3

我碰到它包含下面的代碼示例這個問題about dealing with DateTime.Now in unit tests的接受的答案:爲什麼將func <T>傳遞給構造函數而不是T?

private readonly Func<DateTime> _nowProvider; 
public SomeClass(Func<DateTime> nowProvider) 
{ 
    _nowProvider = nowProvider; 
} 

public bool Foo() 
{ 
    return (_nowProvider().DayOfWeek == DayOfWeek.Sunday); 
} 

實例化爲例如:

var s = new SomeClass(() => DateTime.Now); 

在C#中,我沒有太多的使用Func<T>所以我想我'd看看at the Microsoft documentation for it其中有以下注釋:

你可以使用這個委託來表示一個方法,可以作爲參數傳遞而不顯式聲明自定義委託。封裝的方法必須與此代理定義的方法簽名相對應。這意味着封裝的方法必須沒有參數並且必須返回一個值。

它爲什麼會在這個例子更利於傳遞Func<DateTime>,實例化爲Class(() => DateTime.Now)到構造

,而不是隻是簡單的實例化Class(DateTime.Now)來構造一個DateTime參數傳遞相反?

根據上面提到的微軟文檔,LINQ的lambda構造函數也採取Func<T>的論點和我的經驗證明它們非常靈活,但我不明白爲什麼?

+2

所以這個類可以隨時調用它來獲得更新的結果。如果你剛剛通過T,價值將永遠是固定的。它還使得測試更容易,因爲您可以輕鬆_cheat_的價值爲了測試 – litelite

+0

這可能對單元測試有意義 – ogomrub

+0

它將允許單元測試提供測試基於時間的函數的特定時間。 –

回答

6

而不是簡單地將DateTime參數實例化爲Class(DateTime.Now)傳遞給構造函數?

因爲值應該是當前日期時間,而不是當實例化類時的值。

當代碼運行時,Func返回代碼的確切執行時間

如果DateTime將存儲在一個字段中,那將是創建時間,而不是現在。


我舉個例子。

假設您在週六23:59:55創建了一個Class的實例。

10秒鐘後,下面剪斷:

(passedDateTime.DayOfWeek == DayOfWeek.Sunday); 

將返回false。

對於提供者,日期時間實際上是星期日 - 它的執行時間。


技術:

DateTime的是一個結構。

將DateTime作爲參數傳遞給方法或構造函數時,它將作爲值傳遞,而不是引用。

因此DateTime將不是最新的,而只是該值的快照。

可以證實這一點你自己:

var dateTime = DateTime.Now; 

System.Threading.Sleep(1000); 
bool equals = dateTime == DateTime.Now; // false 
3

該模式允許的日期和時間要麼由DateTime.Now提供,在正常運行期間,或在單元測試過程進行嚴格控制。

例如,想要測試基於時間的功能的單元測試可以驗證在每次調用之間調用兩次函數兩次以上(共同緩存技術)的情況下返回的結果是正確的,而無需等待通話間隔5分鐘。


這也是「控制反轉」模式的一個例子。通過構造函數將檢索數據的方法「注入」到類中。然後,該類可以自由使用任何注入方法,而無需意識到其實現。

+0

「然後,該類可以自由使用任何方法注入,而無需意識到它的實現。」絕對!偉大的一點。 – Mafii

+2

在編碼的用戶界面測試或甚至交互式用戶測試期間,這也非常有用,您可以在此查看一天中特定時間內的情況。可以將一個選項添加到UI中,以「強制」系統認爲它是一天中的特定時間。 –

2

我附上了一個小例子,說明在單元測試中這可能如何。

如果您無法提供不同的「現在」,則單元測試將根據其運行時間而有所不同。

[TestMethod] 
    public void TestFoo() 
    { 
     var obj = new SomeClass(() => DateTime.Now); 

     //Only true on sundays 
     Assert.IsTrue(obj.Foo()); 

     //This is sunday 
     obj = new SomeClass(() => new DateTime(2017, 7, 30)); 
     //This will be always true 
     Assert.IsTrue(obj.Foo()); 

     //This is not sunday 
     obj = new SomeClass(() => new DateTime(2017, 7, 29)); 
     //This will be always false 
     Assert.IsFalse(obj.Foo()); 
    } 
相關問題