[C#] 단위 테스트 : DateTime.Now

‘현재 시간’이 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.CurrentTest 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
}

면책 조항-나는 두더지에서 일합니다


답변

이를위한 몇 가지 옵션이 있습니다.

  1. 모의 프레임 워크를 사용하고 DateTimeService를 사용하십시오 (작은 랩퍼 클래스를 구현하고 프로덕션 코드에 삽입하십시오). 래퍼 구현은 DateTime에 액세스하며 테스트에서 래퍼 클래스를 조롱 할 수 있습니다.

  2. Typemock Isolator를 사용하면 DateTime위조 할 수 있으며 테스트중인 코드를 변경할 필요가 없습니다.

  3. Moles를 사용하면 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);
});