简短的回答是否定的。如果您想围绕一些基本界面进行调整,您要么需要设置操作系统并在测试运行之前更改时间(这可能会产生非常不可预测的结果),要么模拟它。其实很简单:
不要使用 DateTime.Now/UtcNow,而是使用您的显式版本。
public interface IDateTimeManager
{
DateTime Now {get;}
}
代码库中的任何位置:
var now = _dateTimeManager.Now;
在测试设置中:
var mockDateTimeManager = new Mock<IDateTimeManager>();
mockDateTimeManager.Setup(x=> x.Now).Returns(new DateTime(2000,1,1));
在设置中:
var container = new Container();
container.RegisterSingleton<IDateTimeManager>(mockDateTimeManager.Object);
如果您是应用程序单独的可执行文件/服务,您可以传递一个设置此管理器初始值的参数:
myapp.exe --start-date "2000-01-01"
public interface IDateTimeManager
{
DateTime UtcNow { get; }
}
internal sealed class DateTimeManager : IDateTimeManager
{
private readonly DateTime _initialTime;
private readonly DateTime _initialOsTime;
public DateTime UtcNow => _initialTime + (DateTime.UtcNow - _initialOsTime);
public DateTimeManager(DateTime initialTime)
{
_initialTime = initialTime;
_initialOsTime = DateTime.UtcNow;
}
}
附言
在这种情况下,控制反转是你最好的朋友。
我得出的结论是,将特定于操作系统的方法替换为我自己的接口比在测试环境中进行修改更容易。例如,在我的项目中,出于同样的原因,所有 File 命名空间经常被嘲笑。我可以用这种方式模拟任何行为。