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);
}