Time travel with TimeProvider
Ak píšete unit testy viete, že používať priamo DateTime.Now nie je dobrý nápad 🤔.
Pravdepodobne máte vo svojom projekte niečo ako IDateTimeProvider, alebo niečo podobné.
🌠 Microsoft po 21 rokoch prišiel s vlastným riešením. Počnúc .NET 8 máme k dispozícii abstraktnú triedu TimeProvider. Tú môžeme injektnúť tam, kde ju potrebujeme a používať ju.
V projekte potom nastavíme použitie na TimeProvider.System.
🧪 V testoch môžeme použiť triedu FakeTimeProvider z knižnice Microsoft.Extensions.Time.Testing.
Táto trieda nám umožní cestovať v čase pri písaní načích testov.
Použite nový TimeProvider namiesto pôvodného DateTime.Now alebo DateTime.UtcNow.
// 👇 Inject time provider
public class Basket(TimeProvider timeProvider, LoyaltyLevel loyaltyLevel)
{
private readonly DateTimeOffset _expireAt = timeProvider.GetUtcNow() // 👈 Use time provider
.AddDays(loyaltyLevel == LoyaltyLevel.Standard ? 1 : 7);
public bool IsExpired => timeProvider.GetUtcNow() > _expireAt; // 👈 Use time provider
}
TimeProvider.System je k dispozícii pre štandardné situáci. Použite ho tam, kde ho potrebujete.
// 👇 Use System time provider in your code
var basket = new Basket(TimeProvider.System, LoyaltyLevel.Gold);V testoch použite FakeTimeProvider a cestujte v čase podľa potreby pomocou metódy Advance.
[Fact]
public void GoldendUserShouldBeExpiredAfter7Days()
{
// 👇 Use fake time provider in your test
var fakeTime = new FakeTimeProvider(DateTimeOffset.UtcNow);
var basket = new Basket(fakeTime, LoyaltyLevel.Gold);
// 👇 Travel in time
fakeTime.Advance(TimeSpan.FromDays(8));
Assert.True(basket.IsExpired);
}