답변:
내가 선호하는 것은 시간을 사용하는 클래스가 실제로 다음과 같은 인터페이스에 의존하도록하는 것입니다.
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
계산에 사용 하고 포함하는 것은 옳다고 생각하지 않습니다. 예를 들어 자정 근처 또는 화요일에만 발생하는 버그를 발견 한 경우 특정 시간을 테스트 할 수있는 능력을 빼앗 깁니다. 현재 시간을 사용하면 이러한 시나리오를 테스트 할 수 없습니다. 아니면 적어도 원할 때마다.
UtcNow
에 코드의 문제 (예 : 비즈니스 로직, UI 등)에 따라 적절하게 조정해야한다는 점을 추가하고 싶었습니다 . 시간대 전반에 걸친 DateTime 조작은 지뢰밭이지만 가장 좋은 첫 걸음은 항상 다음으로 시작하는 것입니다. UTC 시간.
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/
간단한 대답 : System.DateTime 도랑 :) 대신 NodaTime을 사용 하고 테스트 라이브러리 : NodaTime.Testing 입니다.
추가 읽기 :
이 상황에 너무 자주 직면 하여 인터페이스를 통해 Now 속성을 노출하는 간단한 너겟을 만들었습니다 .
public interface IDateTimeTools
{
DateTime Now { get; }
}
물론 구현은 매우 간단합니다.
public class DateTimeTools : IDateTimeTools
{
public DateTime Now => DateTime.Now;
}
따라서 프로젝트에 너겟을 추가 한 후 단위 테스트에서 사용할 수 있습니다.
GUI Nuget Package Manager에서 직접 또는 다음 명령을 사용하여 모듈을 설치할 수 있습니다.
Install-Package -Id DateTimePT -ProjectName Project
그리고 Nuget의 코드는 여기에 있습니다 .
조건부 컴파일을 사용하여 디버그 / 배포 중에 발생하는 작업을 제어하는 것을 고려해 보셨습니까?
예 :
DateTime date;
#if DEBUG
date = new DateTime(2008, 09, 04);
#else
date = DateTime.Now;
#endif
실패하면 속성을 노출하여 조작 할 수 있습니다. 이것은 테스트 가능한 코드 를 작성해야하는 도전의 일부입니다 .
저의 큰 부분은 블레어의 접근 방식을 선호 할 것 입니다. 이를 통해 테스트에 도움이되도록 코드의 일부를 "핫 플러그"할 수 있습니다. 모든 설계 원리는 다음 없습니다 다릅니다 어떤 캡슐 은 그냥 아무도 외부에서 그것을보고, 테스트 코드가 생산 코드에 차이가 없습니다.
하지만이 예제에서는 생성과 인터페이스가 많은 작업처럼 보일 수 있습니다 (그래서 조건부 컴파일을 선택했습니다).
DateTime
의 Now
와 Today
등