為什麼要寫一個DateTime的Stub呢?
在有些情況必需判斷今天,如不同的節日,不同的Logo,因為System.DateTime.Now或Today,沒有辦法模擬,寫Unit Test時就沒有辦法測各個時間點的反應,總不可能做個測試要改系統時間吧。
為什麼要寫一個DateTime的Stub呢?
在有些情況必需判斷今天,如不同的節日,不同的Logo,因為System.DateTime.Now或Today,沒有辦法模擬,寫Unit Test時就沒有辦法測各個時間點的反應,總不可能做個測試要改系統時間吧。
建立DateTImeProdiver的虛擬類別,來包裝DateTime,有二個實作DefaultDateTimeProdiver是一般的Code使用,TestDateTimeProvider是Test的Code使用。
圖一 DateTiemProdiver的類別圖
使用
所有呼叫System.DateTime.Now或Today,都改成用System.DateTimeProdiver.Current.Now或Today,為了方便測試,改變一點寫法不為過吧。
DateTime today = DateTimeProvider.Current.Today;
測試時
//測試時改變DateTimeProvider的實例,以便摸擬不同時間的變化
DateTimeProvider.Current = new TestDateTimeProvider(new DateTime(2010, 1, 1));
Assert.AreEqual(LogoHelper.GetLogo(),"元旦.png");
DateTimeProvider.Current = new TestDateTimeProvider(new DateTime(2010, 2, 14));
Assert.AreEqual(LogoHelper.GetLogo(), "情人節.png");
原始碼
DateTImeProdiver與DefaultDateTimeProdiver
//使用System,方便呼叫
namespace System
{
/// <summary>
/// 為了單元測試增加的提供者,如DateTime.Now,無法Mock,所以多一層以便測試
/// </summary>
public abstract class DateTimeProvider
{
/// <summary>
/// 預設的DateTimeProvider
/// </summary>
private class DefaultDateTimeProvider : DateTimeProvider
{
/// <summary>
/// 使用獨體模式
/// </summary>
private static DateTimeProvider instance;
static DefaultDateTimeProvider()
{
instance = new DefaultDateTimeProvider();
}
public static DateTimeProvider Instance
{
get
{
return instance;
}
}
public override DateTime Now
{
get { return DateTime.Now; }
}
public override DateTime UtcNow
{
get { return DateTime.UtcNow; }
}
public override DateTime Today
{
get { return DateTime.Today; }
}
}
/// <summary>
/// 預設使用DefaultDateTimeProvider的獨體值
/// </summary>
private static DateTimeProvider current = DefaultDateTimeProvider.Instance;
/// <summary>
/// 可替換成別的DateTimeProvider
/// </summary>
public static DateTimeProvider Current
{
get { return DateTimeProvider.current; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
DateTimeProvider.current = value;
}
}
public abstract DateTime Now { get; }
public abstract DateTime UtcNow { get; }
public abstract DateTime Today { get; }
}
}
TestDateTImeProdiver
/// <summary>
/// 測試專用的DateTimeProvider
/// </summary>
public class TestDateTimeProvider : DateTimeProvider
{
private DateTime datetime;
public TestDateTimeProvider(DateTime datetime)
{
this.datetime = datetime;
}
public override DateTime Now
{
get { return datetime; }
}
public override DateTime UtcNow
{
get { return datetime.ToUniversalTime(); }
}
public override DateTime Today
{
get { return datetime.Date; }
}
}