TailoredApps.Shared.DateTime¶
Opis¶
Biblioteka rozwiązuje jeden z fundamentalnych problemów testowalności aplikacji .NET — bezpośrednie użycie System.DateTime.Now w kodzie produkcyjnym, które uniemożliwia pisanie deterministycznych testów jednostkowych.
TailoredApps.Shared.DateTime dostarcza interfejs IDateTimeProvider i jego domyślną implementację DateTimeProvider. Zamiast wywoływać DateTime.Now wprost, wstrzykujesz IDateTimeProvider przez DI i wywołujesz provider.Now. W testach wymieniasz implementację na mock zwracający dowolny punkt w czasie — dzięki temu testy są powtarzalne i niezależne od zegara systemowego.
Instalacja¶
Rejestracja w DI¶
// Program.cs
using TailoredApps.Shared.DateTime;
builder.Services.AddSingleton<IDateTimeProvider, DateTimeProvider>();
Przykład użycia¶
Kod produkcyjny¶
public class OrderService
{
private readonly IDateTimeProvider _dateTime;
public OrderService(IDateTimeProvider dateTime)
{
_dateTime = dateTime;
}
public Order CreateOrder(string customerId, decimal amount)
{
return new Order
{
Id = Guid.NewGuid(),
CustomerId = customerId,
Amount = amount,
CreatedAt = _dateTime.UtcNow, // zamiast DateTime.UtcNow
ExpiresAt = _dateTime.UtcNow.AddDays(30)
};
}
public bool IsOrderExpired(Order order)
{
return order.ExpiresAt < _dateTime.UtcNow;
}
}
Test jednostkowy (Moq)¶
using Moq;
using TailoredApps.Shared.DateTime;
using Xunit;
public class OrderServiceTests
{
[Fact]
public void CreateOrder_ShouldSetCreatedAtToCurrentUtcTime()
{
// Arrange
var fixedTime = new DateTime(2024, 6, 1, 12, 0, 0, DateTimeKind.Utc);
var dateTimeMock = new Mock<IDateTimeProvider>();
dateTimeMock.Setup(d => d.UtcNow).Returns(fixedTime);
var service = new OrderService(dateTimeMock.Object);
// Act
var order = service.CreateOrder("customer-1", 99.99m);
// Assert
Assert.Equal(fixedTime, order.CreatedAt);
Assert.Equal(fixedTime.AddDays(30), order.ExpiresAt);
}
[Fact]
public void IsOrderExpired_WhenExpiresInPast_ReturnsTrue()
{
// Arrange
var now = new DateTime(2024, 6, 1, DateTimeKind.Utc);
var dateTimeMock = new Mock<IDateTimeProvider>();
dateTimeMock.Setup(d => d.UtcNow).Returns(now);
var service = new OrderService(dateTimeMock.Object);
var expiredOrder = new Order { ExpiresAt = now.AddDays(-1) };
// Act & Assert
Assert.True(service.IsOrderExpired(expiredOrder));
}
}
API Reference¶
| Typ | Rodzaj | Opis |
|---|---|---|
IDateTimeProvider |
Interfejs | Główny kontrakt — wszystkie właściwości do pobierania czasu |
DateTimeProvider |
Klasa | Implementacja produkcyjna — deleguje do System.DateTime |
IDateTimeProvider.Now |
Właściwość | Aktualny czas lokalny (DateTime.Now) |
IDateTimeProvider.UtcNow |
Właściwość | Aktualny czas UTC (DateTime.UtcNow) |
IDateTimeProvider.Today |
Właściwość | Aktualna data lokalna (DateTime.Today) |
IDateTimeProvider.UtcToday |
Właściwość | Aktualna data UTC (DateTime.UtcNow.Date) |
IDateTimeProvider.TimeOfDay |
Właściwość | Pora dnia (lokalnie) jako TimeSpan |
IDateTimeProvider.UtcTimeOfDaty |
Właściwość | Pora dnia UTC jako TimeSpan |
🤖 AI Agent Prompt¶
## TailoredApps.Shared.DateTime — Instrukcja dla agenta AI
Używasz biblioteki TailoredApps.Shared.DateTime w projekcie .NET.
### Rejestracja
```csharp
// Program.cs
builder.Services.AddSingleton<IDateTimeProvider, DateTimeProvider>();
Użycie¶
- Nigdy nie używaj
DateTime.NowaniDateTime.UtcNowbezpośrednio w kodzie produkcyjnym - Wstrzykuj
IDateTimeProviderprzez konstruktor - Używaj
provider.UtcNowdla timestampów w bazie danych - Używaj
provider.Nowtylko gdy potrzebujesz czasu lokalnego (np. do wyświetlania)
// ✅ Poprawnie
public class MyService
{
private readonly IDateTimeProvider _dateTime;
public MyService(IDateTimeProvider dateTime) => _dateTime = dateTime;
public DateTime GetExpiry() => _dateTime.UtcNow.AddHours(1);
}
// ❌ Niepoprawnie
public class MyService
{
public DateTime GetExpiry() => DateTime.UtcNow.AddHours(1); // nie testowalny!
}
Zasady¶
- Zawsze używaj
IDateTimeProviderzamiastSystem.DateTimebezpośrednio - W testach mockuj interfejs, aby zwracał stały punkt w czasie
- Preferuj
UtcNow/UtcTodaydla wartości zapisywanych w bazie danych ```