'현재 시간'이 DateTime과 다를 것으로 예상되는 단위 테스트가 있습니다. 지금 컴퓨터 시간을 변경하고 싶지 않습니다.
이를 달성하기위한 최선의 전략은 무엇입니까?
DateTime
. 이것은 당신이 테스트하는 것이 될 것이고, 기존의 메소드는 단순히 오버로드를 호출합니다 now
.
'현재 시간'이 DateTime과 다를 것으로 예상되는 단위 테스트가 있습니다. 지금 컴퓨터 시간을 변경하고 싶지 않습니다.
이를 달성하기위한 최선의 전략은 무엇입니까?
DateTime
. 이것은 당신이 테스트하는 것이 될 것이고, 기존의 메소드는 단순히 오버로드를 호출합니다 now
.
답변:
최선의 전략이다 추상화에 현재 시간을 포장하고 소비자에 그 추상화를 주입 .
또는 시간 추상화를 앰비언트 컨텍스트 로 정의 할 수도 있습니다 .
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()
.
DateTime.UtcNow
는 있지만 코드 검토는 좋은 생각입니다.
이것들은 모두 좋은 답변입니다. 이것은 내가 다른 프로젝트에서 한 일입니다.
용법:
오늘의 실제 날짜 시간 가져 오기
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에서 일합니다
@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);
});
스레드 안전을 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;
}
}
사용 예 :
[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();
}
Now
합니다.
AsyncLocal
오히려 사용했습니다ThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
나는이 같은 문제에 부딪 쳤지 만이 문제를 해결하는 Microsoft의 연구 프로젝트를 찾았습니다.
http://research.microsoft.com/en-us/projects/moles/
Moles는 델리게이트를 기반으로하는 .NET의 테스트 스텁 및 우회 용 경량 프레임 워크입니다. 봉인 된 유형의 비가 상 / 정적 방법을 포함하여 .NET 방법을 우회하는 데 두더지를 사용할 수 있습니다.
// Let's detour DateTime.Now
MDateTime.NowGet = () => new DateTime(2000,1, 1);
if (DateTime.Now == new DateTime(2000, 1, 1);
{
throw new Exception("Wahoo we did it!");
}
샘플 코드는 원본에서 수정되었습니다.
나는 다른 것을 제안하고 DateTime을 공급자로 추상화했습니다. 방금 잘못 느꼈고 테스트하기에는 너무 많은 것 같았습니다. 오늘 저녁 내 개인 프로젝트에 이것을 구현할 것입니다.
TypeMock 으로 조롱 DateTime.Now
하는 것에 대한 특별 참고 사항 ...
이 값을 DateTime.Now
제대로 조롱하려면 변수 값을 변수에 배치해야합니다. 예를 들면 다음과 같습니다.
작동하지 않습니다.
if ((DateTime.Now - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))
그러나 이것은 다음을 수행합니다.
var currentDateTime = DateTime.Now;
if ((currentDateTime - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))
아무도 가장 분명한 방법 중 하나를 제안하지 않은 것에 놀랐습니다.
public class TimeDependentClass
{
public void TimeDependentMethod(DateTime someTime)
{
if (GetCurrentTime() > someTime) DoSomething();
}
protected virtual DateTime GetCurrentTime()
{
return DateTime.Now; // or UtcNow
}
}
그런 다음 테스트 에서이 방법을 간단히 재정의 할 수 있습니다.
나는 TimeProvider
어떤 경우에는 수업을 주입하는 것과 비슷 하지만 다른 경우에는 이것으로 충분합니다. TimeProvider
그래도 여러 클래스에서이를 재사용 해야하는 경우 버전을 선호합니다 .
편집 : 관심있는 사람을 위해 이것을 클래스에 "심"을 추가하는 것입니다. 클래스의 코드를 실제로 변경하지 않고도 테스트 목적 또는 기타 방식으로 클래스를 수정하는 동작에 연결할 수있는 지점입니다.
DateTimeProvider가 IDisposable을 구현할 때 좋습니다.
public class DateTimeProvider : IDisposable
{
[ThreadStatic]
private static DateTime? _injectedDateTime;
private DateTimeProvider()
{
}
/// <summary>
/// Gets DateTime now.
/// </summary>
/// <value>
/// The DateTime now.
/// </value>
public static DateTime Now
{
get
{
return _injectedDateTime ?? DateTime.Now;
}
}
/// <summary>
/// Injects the actual date time.
/// </summary>
/// <param name="actualDateTime">The actual date time.</param>
public static IDisposable InjectActualDateTime(DateTime actualDateTime)
{
_injectedDateTime = actualDateTime;
return new DateTimeProvider();
}
public void Dispose()
{
_injectedDateTime = null;
}
}
다음으로, 유닛 테스트를 위해 가짜 DateTime을 주입 할 수 있습니다
using (var date = DateTimeProvider.InjectActualDateTime(expectedDateTime))
{
var bankAccount = new BankAccount();
bankAccount.DepositMoney(600);
var lastTransaction = bankAccount.Transactions.Last();
Assert.IsTrue(expectedDateTime.Equals(bankAccount.Transactions[0].TransactionDate));
}
DateTimeProvider 예제 예 참조
이를 수행하는 한 가지 확실한 방법은 VirtualTime을 주입하는 것입니다. 시간을 제어 할 수 있습니다. 먼저 VirtualTime을 설치하십시오
Install-Package VirtualTime
예를 들어 DateTime에 대한 모든 호출에서 5 배 빠르게 이동하는 시간을 만들 수 있습니다.
var DateTime = DateTime.Now.ToVirtualTime(5);
시간 이동을 느리게하려면 (예 : 5 배 느리게)
var DateTime = DateTime.Now.ToVirtualTime(0.5);
시간을 멈추게하려면
var DateTime = DateTime.Now.ToVirtualTime(0);
뒤로 이동하는 것은 아직 테스트되지 않았습니다
다음은 샘플 테스트입니다.
[TestMethod]
public void it_should_make_time_move_faster()
{
int speedOfTimePerMs = 1000;
int timeToPassMs = 3000;
int expectedElapsedVirtualTime = speedOfTimePerMs * timeToPassMs;
DateTime whenTimeStarts = DateTime.Now;
ITime time = whenTimeStarts.ToVirtualTime(speedOfTimePerMs);
Thread.Sleep(timeToPassMs);
DateTime expectedTime = DateTime.Now.AddMilliseconds(expectedElapsedVirtualTime - timeToPassMs);
DateTime virtualTime = time.Now;
Assert.IsTrue(TestHelper.AreEqualWithinMarginOfError(expectedTime, virtualTime, MarginOfErrorMs));
}
여기에서 더 많은 테스트를 확인할 수 있습니다.
DateTime.Now.ToVirtualTime 확장이 제공하는 것은 ITime에 의존하는 메서드 / 클래스에 전달하는 ITime의 인스턴스입니다. 일부 DateTime.Now.ToVirtualTime은 선택한 DI 컨테이너에 설정됩니다.
다음은 클래스 위탁자에게 주입하는 또 다른 예입니다.
public class AlarmClock
{
private ITime DateTime;
public AlarmClock(ITime dateTime, int numberOfHours)
{
DateTime = dateTime;
SetTime = DateTime.UtcNow.AddHours(numberOfHours);
Task.Run(() =>
{
while (!IsAlarmOn)
{
IsAlarmOn = (SetTime - DateTime.UtcNow).TotalMilliseconds < 0;
}
});
}
public DateTime SetTime { get; set; }
public bool IsAlarmOn { get; set; }
}
[TestMethod]
public void it_can_be_injected_as_a_dependency()
{
//virtual time has to be 1000*3.75 faster to get to an hour
//in 1000 ms real time
var dateTime = DateTime.Now.ToVirtualTime(1000 * 3.75);
var numberOfHoursBeforeAlarmSounds = 1;
var alarmClock = new AlarmClock(dateTime, numberOfHoursBeforeAlarmSounds);
Assert.IsFalse(alarmClock.IsAlarmOn);
System.Threading.Thread.Sleep(1000);
Assert.IsTrue(alarmClock.IsAlarmOn);
}
정적 SystemTime 객체를 사용했지만 병렬 단위 테스트를 실행하는 데 문제가 발생했습니다. Henk van Boeijen의 솔루션을 사용하려고 시도했지만 생성 된 비동기 스레드에서 문제가 발생했으며 다음과 유사한 방식으로 AsyncLocal을 사용했습니다.
public static class Clock
{
private static Func<DateTime> _utcNow = () => DateTime.UtcNow;
static AsyncLocal<Func<DateTime>> _override = new AsyncLocal<Func<DateTime>>();
public static DateTime UtcNow => (_override.Value ?? _utcNow)();
public static void Set(Func<DateTime> func)
{
_override.Value = func;
}
public static void Reset()
{
_override.Value = null;
}
}
https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08 에서 공급
오래된 질문이지만 여전히 유효합니다.
내 접근 방식은 새로운 인터페이스와 클래스를 만들어 System.DateTime.Now
호출 을 래핑하는 것입니다.
public interface INow
{
DateTime Execute();
}
public sealed class Now : INow
{
public DateTime Execute()
{
return DateTime.Now
}
}
이 인터페이스는 현재 날짜와 시간을 가져와야하는 모든 클래스에 삽입 할 수 있습니다. 이 예제에는 현재 날짜 및 시간에 시간 범위를 추가하는 클래스가 있습니다 (단위 테스트 가능한 System.DateTime.Now.Add (TimeSpan)).
public interface IAddTimeSpanToCurrentDateAndTime
{
DateTime Execute(TimeSpan input);
}
public class AddTimeSpanToCurrentDateAndTime : IAddTimeSpanToCurrentDateAndTime
{
private readonly INow _now;
public AddTimeSpanToCurrentDateAndTime(INow now)
{
this._now = now;
}
public DateTime Execute(TimeSpan input)
{
var currentDateAndTime = this._now.Execute();
return currentDateAndTime.Add(input);
}
}
테스트가 올바르게 작동하는지 확인할 수 있습니다. NUnit과 Moq를 사용하고 있지만 모든 테스트 프레임 워크는
public class Execute
{
private Moq.Mock<INow> _nowMock;
private AddTimeSpanToCurrentDateAndTime _systemUnderTest;
[SetUp]
public void Initialize()
{
this._nowMock = new Moq.Mock<INow>(Moq.MockBehavior.Strict);
this._systemUnderTest = AddTimeSpanToCurrentDateAndTime(
this._nowMock.Object);
}
[Test]
public void AddTimeSpanToCurrentDateAndTimeExecute0001()
{
// arrange
var input = new TimeSpan(911252);
// arrange : mocks
this._nowMock
.Setup(a => a.Execute())
.Returns(new DateTime(348756););
// arrange : expected
var expected = new DateTime(911252 + 348756);
// act
var actual = this._systemUnderTest.Execute(input).Result;
// assert
Assert.Equals(actual, expected);
}
}
이 패턴은 같은 외부 요인에 따라 어떤 기능 작동 System.Random.Next()
, System.DateTime.Now.UtcNow
, System.Guid.NewGuid()
등
추가 예제는 https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core 를 참조 하거나 https://www.nuget.org/packages/Stamina.Core 너겟 패키지를 참조하십시오 .
당신은 당신이 사용하는 테스트 클래스를 변경할 수 있습니다 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();
}
}
나는 같은 문제가 있었지만 같은 클래스에서 설정된 날짜 시간을 사용해서는 안된다고 생각했습니다. 하루 남용으로 이어질 수 있기 때문입니다. 그래서 나는 공급자를 사용했다
public class DateTimeProvider
{
protected static DateTime? DateTimeNow;
protected static DateTime? DateTimeUtcNow;
public DateTime Now
{
get
{
return DateTimeNow ?? System.DateTime.Now;
}
}
public DateTime UtcNow
{
get
{
return DateTimeUtcNow ?? System.DateTime.UtcNow;
}
}
public static DateTimeProvider DateTime
{
get
{
return new DateTimeProvider();
}
}
protected DateTimeProvider()
{
}
}
테스트를 위해 테스트 프로젝트에서 정해진 일을 처리하는 도우미를 만들었습니다.
public class MockDateTimeProvider : DateTimeProvider
{
public static void SetNow(DateTime now)
{
DateTimeNow = now;
}
public static void SetUtcNow(DateTime utc)
{
DateTimeUtcNow = utc;
}
public static void RestoreAsDefault()
{
DateTimeNow = null;
DateTimeUtcNow = null;
}
}
코드에서
var dateTimeNow = DateTimeProvider.DateTime.Now //not DateTime.Now
var dateTimeUtcNow = DateTimeProvider.DateTime.UtcNow //not DateTime.UtcNow
그리고 시험에
[Test]
public void Mocked_Now()
{
DateTime now = DateTime.Now;
MockDateTimeProvider.SetNow(now); //set to mock
Assert.AreEqual(now, DateTimeProvider.DateTime.Now);
Assert.AreNotEqual(now, DateTimeProvider.DateTime.UtcNow);
}
[Test]
public void Mocked_UtcNow()
{
DateTime utcNow = DateTime.UtcNow;
MockDateTimeProvider.SetUtcNow(utcNow); //set to mock
Assert.AreEqual(utcNow, DateTimeProvider.DateTime.UtcNow);
Assert.AreNotEqual(utcNow, DateTimeProvider.DateTime.Now);
}
그러나 한 가지만 기억해야합니다. 때로는 실제 DateTime과 공급자의 DateTime이 동일하게 작동하지 않습니다.
[Test]
public void Now()
{
Assert.AreEqual(DateTime.Now.Kind, DateTimeProvider.DateTime.Now.Kind);
Assert.LessOrEqual(DateTime.Now, DateTimeProvider.DateTime.Now);
Assert.LessOrEqual(DateTimeProvider.DateTime.Now - DateTime.Now, TimeSpan.FromMilliseconds(1));
}
지연이 최대 TimeSpan.FromMilliseconds (0.00002) 가 될 것이라고 가정했습니다 . 그러나 대부분의 경우 훨씬 적습니다.
MockSamples 에서 샘플 찾기
여기이 질문에 대한 답이 있습니다. 'Ambient Context'패턴과 IDisposable을 결합합니다. 따라서 일반적인 프로그램 코드에서 DateTimeProvider.Current를 사용할 수 있으며 테스트에서는 using 문으로 범위를 재정의합니다.
using System;
using System.Collections.Immutable;
namespace ambientcontext {
public abstract class DateTimeProvider : IDisposable
{
private static ImmutableStack<DateTimeProvider> stack = ImmutableStack<DateTimeProvider>.Empty.Push(new DefaultDateTimeProvider());
protected DateTimeProvider()
{
if (this.GetType() != typeof(DefaultDateTimeProvider))
stack = stack.Push(this);
}
public static DateTimeProvider Current => stack.Peek();
public abstract DateTime Today { get; }
public abstract DateTime Now {get; }
public void Dispose()
{
if (this.GetType() != typeof(DefaultDateTimeProvider))
stack = stack.Pop();
}
// Not visible Default Implementation
private class DefaultDateTimeProvider : DateTimeProvider {
public override DateTime Today => DateTime.Today;
public override DateTime Now => DateTime.Now;
}
}
}
위의 DateTimeProvider를 단위 테스트에서 사용하는 방법은 다음과 같습니다.
using System;
using Xunit;
namespace ambientcontext
{
public class TestDateTimeProvider
{
[Fact]
public void TestDateTime()
{
var actual = DateTimeProvider.Current.Today;
var expected = DateTime.Today;
Assert.Equal<DateTime>(expected, actual);
using (new MyDateTimeProvider(new DateTime(2012,12,21)))
{
Assert.Equal(2012, DateTimeProvider.Current.Today.Year);
using (new MyDateTimeProvider(new DateTime(1984,4,4)))
{
Assert.Equal(1984, DateTimeProvider.Current.Today.Year);
}
Assert.Equal(2012, DateTimeProvider.Current.Today.Year);
}
// Fall-Back to Default DateTimeProvider
Assert.Equal<int>(expected.Year, DateTimeProvider.Current.Today.Year);
}
private class MyDateTimeProvider : DateTimeProvider
{
private readonly DateTime dateTime;
public MyDateTimeProvider(DateTime dateTime):base()
{
this.dateTime = dateTime;
}
public override DateTime Today => this.dateTime.Date;
public override DateTime Now => this.dateTime;
}
}
}
ITimeProvider
우리는 그것을 사용하여 다른 프로젝트의 다른 프로젝트에서 참조 해야하는 공유 프로젝트를 공유 했습니다. 그러나 이것은 의존성 통제를 복잡하게 만들었다 .
ITimeProvider
.NET 프레임 워크 에서을 검색했습니다 . 우리는 NuGet 패키지를 검색하고, 발견 한 작동하지 않을 수 있습니다 DateTimeOffset
.
따라서 표준 라이브러리 유형에만 의존하는 자체 솔루션을 개발했습니다. 의 인스턴스를 사용하고 Func<DateTimeOffset>
있습니다.
public class ThingThatNeedsTimeProvider
{
private readonly Func<DateTimeOffset> now;
private int nextId;
public ThingThatNeedsTimeProvider(Func<DateTimeOffset> now)
{
this.now = now;
this.nextId = 1;
}
public (int Id, DateTimeOffset CreatedAt) MakeIllustratingTuple()
{
return (nextId++, now());
}
}
오토 팩
builder.RegisterInstance<Func<DateTimeOffset>>(() => DateTimeOffset.Now);
( 향후 편집자 : 사례를 여기에 추가하십시오 ).
public void MakeIllustratingTuple_WhenCalled_FillsCreatedAt()
{
DateTimeOffset expected = CreateRandomDateTimeOffset();
DateTimeOffset StubNow() => expected;
var thing = new ThingThatNeedsTimeProvider(StubNow);
var (_, actual) = thing.MakeIllustratingTuple();
Assert.AreEqual(expected, actual);
}
덜 전문적이지만 간단한 솔루션은 소비자 방법에서 DateTime 매개 변수를 만들 수 있습니다. 예를 들어 SampleMethod와 같은 make 메서드 대신 SampleMethod1을 매개 변수로 만듭니다 .SampleMethod1 테스트가 더 쉽습니다.
public void SampleMethod()
{
DateTime anotherDateTime = DateTime.Today.AddDays(-10);
if ((DateTime.Now-anotherDateTime).TotalDays>10)
{
}
}
public void SampleMethod1(DateTime dateTimeNow)
{
DateTime anotherDateTime = DateTime.Today.AddDays(-10);
if ((dateTimeNow - anotherDateTime).TotalDays > 10)
{
}
}