正常的解決方案是將其隱藏在接口後面。如何在單元測試中模擬DateTime.Now?
public class RecordService
{
private readonly ISystemTime systemTime;
public RecordService(ISystemTime systemTime)
{
this.systemTime = systemTime;
}
public void RouteRecord(Record record)
{
if (record.Created <
systemTime.CurrentTime().AddMonths(-2))
{
// process old record
}
// process the record
}
}
在單元測試中,你可以使用模擬對象,並決定什麼返回
[TestClass]
public class When_old_record_is_processed
{
[TestMethod]
public void Then_it_is_moved_into_old_records_folder()
{
var systemTime = A.Fake<ISystemTime>();
A.CallTo(() => system.Time.CurrentTime())
.Returns(DateTime.Now.AddYears(-1));
var record = new Record(DateTime.Now);
var service = new RecordService(systemTime);
service.RouteRecord(record);
// Asserts...
}
}
我不喜歡注入另一個接口到我的課堂只是爲了獲得當前的時間。對於這樣一個小問題,感覺過於沉重。解決方案是使用靜態類與公共功能。
public static class SystemTime
{
public static Func<DateTime> Now =() => DateTime.Now;
}
現在我們可以刪除ISystemTime注射RecordService看起來像這樣
public class RecordService
{
public void RouteRecord(Record record)
{
if (record.Created < SystemTime.Now.AddMonths(-2))
{
// process old record
}
// process the record
}
}
在單元測試中,我們可以很容易地嘲笑系統時間。
[TestClass]
public class When_old_record_is_processed
{
[TestMethod]
public void Then_it_is_moved_into_old_records_folder()
{
SystemTime.Now =() => DateTime.Now.AddYears(-1);
var record = new Record(DateTime.Now);
var service = new RecordService();
service.RouteRecord(record);
// Asserts...
}
}
當然,這一切都有一個缺點。您正在使用公共字段(The HORROR!),因此沒有人阻止您編寫這樣的代碼。
public class RecordService
{
public void RouteRecord(Record record)
{
SystemTime.Now =() => DateTime.Now.AddYears(10);
}
}
另外我認爲,教育開發人員比創建抽象是爲了保護他們免於犯錯。其他可能的問題與運行測試有關。如果您忘記將功能恢復到原始狀態,則可能會影響其他測試。這取決於單元測試運行器執行測試的方式。 您可以使用相同的邏輯來嘲笑文件系統操作
public static class FileSystem
{
public static Action<string, string> MoveFile = File.Move;
}
在實施這種利用公共職能的功能(嘲諷的時候,簡單的文件系統操作),我的意見是完全可以接受的。它使得代碼更易於閱讀,減少了依賴性,並且很容易在單元測試中模擬。
你用過moq嗎? (或任何其他模擬框架)http://code.google.com/p/moq/ – Magrangs 2012-03-30 11:38:04
我修正了您的代碼的格式。請將它與您的版本進行比較,以瞭解如何正確執行此操作。 – 2012-03-30 11:41:04
@Magrangs:與所有其他「正常」嘲諷框架一樣,Moq不能嘲諷像「DateTime.Now」這樣的靜態方法和屬性。 – 2012-03-30 11:42:07