[c#] 테스트 중에 DateTime.Now를 덮어 쓰는 좋은 방법은 무엇입니까?

오늘 날짜에 의존하여 미래의 일을 올바르게 계산하는 (C #) 코드가 있습니다. 테스트에서 오늘 날짜를 사용하면 테스트에서 계산을 반복해야하는데 옳지 않다고 생각합니다. 결과가 알려진 값인지 테스트 할 수 있도록 테스트 내에서 알려진 값으로 날짜를 설정하는 가장 좋은 방법은 무엇입니까?



답변

내가 선호하는 것은 시간을 사용하는 클래스가 실제로 다음과 같은 인터페이스에 의존하도록하는 것입니다.

interface IClock
{
    DateTime Now { get; }
}

구체적인 구현으로

class SystemClock: IClock
{
     DateTime Now { get { return DateTime.Now; } }
}

그런 다음 원하는 경우 테스트를 위해 원하는 다른 종류의 시계를 제공 할 수 있습니다.

class StaticClock: IClock
{
     DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}

클럭에 의존하는 클래스에 클럭을 제공하는 데 약간의 오버 헤드가있을 수 있지만, 이는 Inversion of Control 컨테이너, 일반 이전 생성자 / 세터 주입 또는 정적 게이트웨이 패턴을 사용하여 여러 종속성 주입 솔루션으로 처리 할 수 ​​있습니다. ).

원하는 시간을 제공하는 객체 또는 방법을 전달하는 다른 메커니즘도 작동하지만 핵심은 시스템 시계 재설정을 피하는 것입니다. 다른 수준에 고통을 줄 수 있기 때문입니다.

또한이를 DateTime.Now계산에 사용 하고 포함하는 것은 옳다고 생각하지 않습니다. 예를 들어 자정 근처 또는 화요일에만 발생하는 버그를 발견 한 경우 특정 시간을 테스트 할 수있는 능력을 빼앗 깁니다. 현재 시간을 사용하면 이러한 시나리오를 테스트 할 수 없습니다. 아니면 적어도 원할 때마다.


답변

Ayende Rahien 은 다소 간단한 정적 방법을 사용 합니다.

public static class SystemTime
{
    public static Func<DateTime> Now = () => DateTime.Now;
}


답변

현재 날짜를 얻는 것과 같은 간단한 작업을 위해 별도의 시계 클래스를 만드는 것은 약간 과잉이라고 생각합니다.

오늘 날짜를 매개 변수로 전달하여 테스트에 다른 날짜를 입력 할 수 있습니다. 이것은 코드를 더 유연하게 만드는 추가적인 이점이 있습니다.


답변

Microsoft Fakes를 사용하여 shim을 만드는 것은 정말 쉬운 방법입니다. 다음 클래스가 있다고 가정합니다.

public class MyClass
{
    public string WhatsTheTime()
    {
        return DateTime.Now.ToString();
    }

}

Visual Studio 2012에서는 Fakes / Shims를 만들려는 어셈블리를 마우스 오른쪽 단추로 클릭하고 “가짜 어셈블리 추가”를 선택하여 테스트 프로젝트에 Fakes 어셈블리를 추가 할 수 있습니다.

가짜 어셈블리 추가

마지막으로 테스트 클래스는 다음과 같습니다.

using System;
using ConsoleApplication11;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DateTimeTest
{
[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestWhatsTheTime()
    {

        using(ShimsContext.Create()){

            //Arrange
            System.Fakes.ShimDateTime.NowGet =
            () =>
            { return new DateTime(2010, 1, 1); };

            var myClass = new MyClass();

            //Act
            var timeString = myClass.WhatsTheTime();

            //Assert
            Assert.AreEqual("1/1/2010 12:00:00 AM",timeString);

        }
    }
}
}


답변

성공적인 단위 테스트의 핵심은 디커플링 입니다. 흥미로운 코드를 외부 종속성에서 분리해야 격리 된 상태에서 테스트 할 수 있습니다. (다행히도 Test-Driven Development는 분리 된 코드를 생성합니다.)

이 경우 외부는 현재 DateTime입니다.

여기서 내 조언은 DateTime을 처리하는 논리를 새 메서드 나 클래스 또는 귀하의 경우에 의미가있는 것으로 추출하고 DateTime을 전달하는 것입니다. 이제 단위 테스트에서 임의의 DateTime을 전달하여 예측 가능한 결과를 생성 할 수 있습니다.


답변

Microsoft Moles ( .NET 용 격리 프레임 워크)를 사용하는 또 다른 하나 입니다.

MDateTime.NowGet = () => new DateTime(2000, 1, 1);

두더지는 모든 .NET 메서드를 대리자로 바꿀 수 있습니다. 두더지는 정적 또는 비가 상 방법을 지원합니다. 두더지는 Pex의 프로파일 러에 의존합니다.


답변

IDisposable 패턴을 사용하는 것이 좋습니다.

[Test]
public void CreateName_AddsCurrentTimeAtEnd()
{
    using (Clock.NowIs(new DateTime(2010, 12, 31, 23, 59, 00)))
    {
        string name = new ReportNameService().CreateName(...);
        Assert.AreEqual("name 2010-12-31 23:59:00", name);
    }
}

여기에 자세히 설명되어 있습니다.
http://www.lesnikowski.com/blog/index.php/testing-datetime-now/