‘현재 시간’이 DateTime과 다를 것으로 예상되는 단위 테스트가 있습니다. 지금 컴퓨터 시간을 변경하고 싶지 않습니다.
이를 달성하기위한 최선의 전략은 무엇입니까?
답변
최선의 전략이다 추상화에 현재 시간을 포장하고 소비자에 그 추상화를 주입 .
또는 시간 추상화를 앰비언트 컨텍스트 로 정의 할 수도 있습니다 .
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;
}
}
이를 통해 다음과 같이 소비 할 수 있습니다.
var now = TimeProvider.Current.UtcNow;
단위 테스트에서는 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;
그러나 정적 상태에서 유닛을 테스트 할 때는 항상 을 호출 하여 조명기 를 분해해야합니다TimeProvider.ResetToDefault()
.
답변
이것들은 모두 좋은 답변입니다. 이것은 내가 다른 프로젝트에서 한 일입니다.
용법:
오늘의 실제 날짜 시간 가져 오기
var today = SystemTime.Now().Date;
DateTime.Now를 사용하는 대신 다음을 사용해야합니다 SystemTime.Now()
. 어려운 변경은 아니지만이 솔루션이 모든 프로젝트에 적합하지는 않습니다.
시간 여행 (미래에 5 년 가야 함)
SystemTime.SetDateTime(today.AddYears(5));
우리의 가짜를 “오늘”얻으십시오
var fakeToday = SystemTime.Now().Date;
날짜 재설정
SystemTime.ResetDateTime();
/// <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;
}
}
답변
두더지 :
[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
}
면책 조항-나는 두더지에서 일합니다
답변
이를위한 몇 가지 옵션이 있습니다.
-
모의 프레임 워크를 사용하고 DateTimeService를 사용하십시오 (작은 랩퍼 클래스를 구현하고 프로덕션 코드에 삽입하십시오). 래퍼 구현은 DateTime에 액세스하며 테스트에서 래퍼 클래스를 조롱 할 수 있습니다.
-
Typemock Isolator를 사용하면 DateTime 을 위조 할 수 있으며 테스트중인 코드를 변경할 필요가 없습니다.
몇 가지 예 :
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; } }
}
아이솔레이터를 사용하여 직접 DateTime 가짜 :
[Test]
public void TestOfDateTime()
{
Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));
var result = new UnderTest().CalculateSomethingBasedOnDate();
}
면책 조항-나는 Typemock에서 일합니다
답변
시스템에 가짜 어셈블리를 추가하십시오 (시스템 참조 => 가짜 어셈블리 추가를 마우스 오른쪽 단추로 클릭하십시오).
테스트 방법을 작성하십시오.
using (ShimsContext.Create())
{
System.Fakes.ShimDateTime.NowGet = () => new DateTime(2014, 3, 10);
MethodThatUsesDateTimeNow();
}
답변
@crabcrusherclamcollector 답변과 관련하여 EF 쿼리에서 해당 방법을 사용할 때 문제가 발생합니다 (System.NotSupportedException : LINQ 표현식 노드 유형 ‘Invoke’는 LINQ to Entities에서 지원되지 않음). 구현을 다음과 같이 수정했습니다.
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;
}
}
}
답변
온 의존하는 코드를 테스트하려면 System.DateTime
의를system.dll
조롱해야합니다.
내가 알고있는 두 가지 프레임 워크가 있습니다. Microsoft 가짜 및 작업복 .
Microsoft 가짜는 Visual Studio 2012 최후 통첩이 필요하며 compton에서 바로 작동합니다.
작업복은 오픈 소스이며 사용하기 매우 쉽습니다. NuGet을 사용하여 다운로드 할 수 있습니다.
다음은 모의를 보여줍니다 System.DateTime
.
Smock.Run(context =>
{
context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));
// Outputs "2000"
Console.WriteLine(DateTime.Now.Year);
});