Ped*_*dro 146 c# datetime unit-testing mstest systemtime
我有一些单元测试,希望'当前时间'与DateTime不同.现在我不想改变计算机的时间.
实现这一目标的最佳策略是什么?
Mar*_*ann 198
在最好的策略是把当前时间的抽象和注入是抽象到消费者.
或者,您也可以将时间抽象定义为环境上下文:
public abstract class TimeProvider
{
private static TimeProvider current =
DefaultTimeProvider.Instance;
public static TimeProvider Current
{
get { return TimeProvider.current; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
TimeProvider.current = value;
}
}
public abstract DateTime UtcNow { get; }
public static void ResetToDefault()
{
TimeProvider.current = DefaultTimeProvider.Instance;
}
}
Run Code Online (Sandbox Code Playgroud)
这将使您能够像这样使用它:
var now = TimeProvider.Current.UtcNow;
Run Code Online (Sandbox Code Playgroud)
在单元测试中,您可以TimeProvider.Current使用Test Double/Mock对象替换.使用Moq的示例:
var timeMock = new Mock<TimeProvider>();
timeMock.SetupGet(tp => tp.UtcNow).Returns(new DateTime(2010, 3, 11));
TimeProvider.Current = timeMock.Object;
Run Code Online (Sandbox Code Playgroud)
但是,当使用静态进行单元测试时,请务必通过调用来拆除夹具TimeProvider.ResetToDefault().
cra*_*TOR 54
这些都是很好的答案,这就是我在另一个项目上所做的:
用法:
获取今天的真实日期时间
var today = SystemTime.Now().Date;
Run Code Online (Sandbox Code Playgroud)
而不是使用DateTime.Now,你需要使用SystemTime.Now()......这不是很难改变,但这个解决方案可能并不适合所有项目.
时间旅行(让我们将来5年)
SystemTime.SetDateTime(today.AddYears(5));
Run Code Online (Sandbox Code Playgroud)
今天"假装"(距离"今天"5年)
var fakeToday = SystemTime.Now().Date;
Run Code Online (Sandbox Code Playgroud)
重置日期
SystemTime.ResetDateTime();
Run Code Online (Sandbox Code Playgroud)
/// <summary>
/// Used for getting DateTime.Now(), time is changeable for unit testing
/// </summary>
public static class SystemTime
{
/// <summary> Normally this is a pass-through to DateTime.Now, but it can be overridden with SetDateTime( .. ) for testing or debugging.
/// </summary>
public static Func<DateTime> Now = () => DateTime.Now;
/// <summary> Set time to return when SystemTime.Now() is called.
/// </summary>
public static void SetDateTime(DateTime dateTimeNow)
{
Now = () => dateTimeNow;
}
/// <summary> Resets SystemTime.Now() to return DateTime.Now.
/// </summary>
public static void ResetDateTime()
{
Now = () => DateTime.Now;
}
}
Run Code Online (Sandbox Code Playgroud)
Pel*_*eli 24
摩尔:
[Test]
public void TestOfDateTime()
{
var firstValue = DateTime.Now;
MDateTime.NowGet = () => new DateTime(2000,1,1);
var secondValue = DateTime.Now;
Assert(firstValue > secondValue); // would be false if 'moleing' failed
}
Run Code Online (Sandbox Code Playgroud)
免责声明 - 我在研究Moles
Eli*_*sha 16
你有一些选择:
使用模拟框架并使用DateTimeService(实现一个小的包装类并将其注入生产代码).包装器实现将访问DateTime,在测试中,您将能够模拟包装器类.
使用Typemock Isolator,它可以伪造DateTime.Now,不需要你更改测试中的代码.
使用Moles,它也可以伪造DateTime.Now并且不需要更改生产代码.
一些例子:
使用Moq的包装类:
[Test]
public void TestOfDateTime()
{
var mock = new Mock<IDateTime>();
mock.Setup(fake => fake.Now)
.Returns(new DateTime(2000, 1, 1));
var result = new UnderTest(mock.Object).CalculateSomethingBasedOnDate();
}
public class DateTimeWrapper : IDateTime
{
public DateTime Now { get { return DateTime.Now; } }
}
Run Code Online (Sandbox Code Playgroud)
使用Isolator直接伪造DateTime:
[Test]
public void TestOfDateTime()
{
Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));
var result = new UnderTest().CalculateSomethingBasedOnDate();
}
Run Code Online (Sandbox Code Playgroud)
免责声明 - 我在Typemock工作
dav*_*ave 10
为System添加假装配(右键单击System reference => Add false assembly).
并写入您的测试方法:
using (ShimsContext.Create())
{
System.Fakes.ShimDateTime.NowGet = () => new DateTime(2014, 3, 10);
MethodThatUsesDateTimeNow();
}
Run Code Online (Sandbox Code Playgroud)
您可以更改正在测试的类以使用 a ,Func<DateTime>它将通过其构造函数参数传递,因此当您在实际代码中创建该类的实例时,您可以传递() => DateTime.UtcNow给该Func<DateTime>参数,并且在测试时,您可以传递您的时间希望测试。
例如:
[TestMethod]
public void MyTestMethod()
{
var instance = new MyClass(() => DateTime.MinValue);
Assert.AreEqual(instance.MyMethod(), DateTime.MinValue);
}
public void RealWorldInitialization()
{
new MyClass(() => DateTime.UtcNow);
}
class MyClass
{
private readonly Func<DateTime> _utcTimeNow;
public MyClass(Func<DateTime> UtcTimeNow)
{
_utcTimeNow = UtcTimeNow;
}
public DateTime MyMethod()
{
return _utcTimeNow();
}
}
Run Code Online (Sandbox Code Playgroud)
关于@crabcrusherclamcollector答案,在EF查询中使用该方法时存在问题(System.NotSupportedException:LINQ to Entities中不支持LINQ表达式节点类型'Invoke').我修改了实现:
public static class SystemTime
{
private static Func<DateTime> UtcNowFunc = () => DateTime.UtcNow;
public static void SetDateTime(DateTime dateTimeNow)
{
UtcNowFunc = () => dateTimeNow;
}
public static void ResetDateTime()
{
UtcNowFunc = () => DateTime.UtcNow;
}
public static DateTime UtcNow
{
get
{
DateTime now = UtcNowFunc.Invoke();
return now;
}
}
}
Run Code Online (Sandbox Code Playgroud)
为了测试依赖于一个代码System.DateTime,则system.dll必须嘲笑。
微软假冒产品需要Visual Studio 2012最后通and,并且可以直接在康普顿中使用。
Smocks是开源的,非常易于使用。可以使用NuGet下载。
下面显示了一个模拟System.DateTime:
Smock.Run(context =>
{
context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));
// Outputs "2000"
Console.WriteLine(DateTime.Now.Year);
});
Run Code Online (Sandbox Code Playgroud)
线程安全SystemClock使用ThreadLocal<T>对我来说很有用.
ThreadLocal<T> 可以在.Net Framework v4.0及更高版本中使用.
/// <summary>
/// Provides access to system time while allowing it to be set to a fixed <see cref="DateTime"/> value.
/// </summary>
/// <remarks>
/// This class is thread safe.
/// </remarks>
public static class SystemClock
{
private static readonly ThreadLocal<Func<DateTime>> _getTime =
new ThreadLocal<Func<DateTime>>(() => () => DateTime.Now);
/// <inheritdoc cref="DateTime.Today"/>
public static DateTime Today
{
get { return _getTime.Value().Date; }
}
/// <inheritdoc cref="DateTime.Now"/>
public static DateTime Now
{
get { return _getTime.Value(); }
}
/// <inheritdoc cref="DateTime.UtcNow"/>
public static DateTime UtcNow
{
get { return _getTime.Value().ToUniversalTime(); }
}
/// <summary>
/// Sets a fixed (deterministic) time for the current thread to return by <see cref="SystemClock"/>.
/// </summary>
public static void Set(DateTime time)
{
if (time.Kind != DateTimeKind.Local)
time = time.ToLocalTime();
_getTime.Value = () => time;
}
/// <summary>
/// Resets <see cref="SystemClock"/> to return the current <see cref="DateTime.Now"/>.
/// </summary>
public static void Reset()
{
_getTime.Value = () => DateTime.Now;
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
[TestMethod]
public void Today()
{
SystemClock.Set(new DateTime(2015, 4, 3));
DateTime expectedDay = new DateTime(2015, 4, 2);
DateTime yesterday = SystemClock.Today.AddDays(-1D);
Assert.AreEqual(expectedDay, yesterday);
SystemClock.Reset();
}
Run Code Online (Sandbox Code Playgroud)