Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Das 📦 Microsoft.Extensions.TimeProvider.Testing NuGet-Paket stellt eine FakeTimeProvider Klasse bereit, die deterministische Tests von Code ermöglicht, der von der Zeit abhängt. Diese gefälschte Implementierung ermöglicht es Ihnen, die Systemzeit innerhalb Ihrer Tests zu steuern, um vorhersehbare und wiederholbare Ergebnisse sicherzustellen.
Gründe für die Verwendung von FakeTimeProvider
Das Testen von Code, der von der aktuellen Zeit abhängt oder Zeitgeber verwendet, kann eine Herausforderung darstellen:
- Nicht deterministische Tests: Tests, die von Echtzeit abhängen, können inkonsistente Ergebnisse erzeugen.
- Langsame Tests: Tests, die darauf warten müssen, dass die tatsächliche Zeit vergeht, können die Testausführung erheblich verlangsamen.
- Rennbedingungen: Zeitabhängige Logik kann Rennbedingungen einführen, die schwer zu reproduzieren sind.
- Randfälle: Das Testen zeitbasierter Logik zu bestimmten Zeiten (z. B. Mitternacht oder Monatsende) ist in Echtzeit schwierig.
Die FakeTimeProvider adressiert diese Herausforderungen durch:
- Bieten vollständige Kontrolle über die aktuelle Zeit.
- Sie können die Zeit sofort ohne Wartezeit voranschreiten.
- Aktivieren von deterministischen Tests des zeitbasierten Verhaltens.
- Dies erleichtert das Testen von Randfällen und Grenzbedingungen.
Loslegen
Um mit FakeTimeProvider zu beginnen, installieren Sie das Microsoft.Extensions.TimeProvider.Testing NuGet-Paket.
dotnet add package Microsoft.Extensions.TimeProvider.Testing
Weitere Informationen finden Sie unter dotnet add package or Manage package dependencies in .NET applications.
Grundlegende Nutzung
Die FakeTimeProvider verlängert TimeProvider, um eine steuerbare Zeit für Tests bereitzustellen.
var fakeTimeProvider = new FakeTimeProvider();
// Get the current time (defaults to January 1, 2000, midnight UTC).
Console.WriteLine($"Start time: {fakeTimeProvider.GetUtcNow()}");
Initialisieren mit einem festgelegten Zeitpunkt
Sie können FakeTimeProvider mit einer bestimmten Startzeit initialisieren.
DateTimeOffset startTime = new(2025, 10, 20, 12, 0, 0, TimeSpan.Zero);
fakeTimeProvider = new FakeTimeProvider(startTime);
Console.WriteLine($"Started at: {fakeTimeProvider.GetUtcNow()}");
Vorlaufzeit
Die Advance Methode verschiebt die Zeit um eine angegebene Dauer vorwärts:
// Advance time by 30 minutes.
fakeTimeProvider.Advance(TimeSpan.FromMinutes(30));
Console.WriteLine($"After advancing 30 minutes: {fakeTimeProvider.GetUtcNow()}");
Konfigurieren der Zeitzone
Legen Sie die lokale Zeitzone für den gefälschten Zeitanbieter fest:
var timeZoneId = OperatingSystem.IsWindows() ? "Pacific Standard Time" : "America/Los_Angeles";
var pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
fakeTimeProvider.SetLocalTimeZone(pacificTimeZone);
var localTime = fakeTimeProvider.GetLocalNow();
Console.WriteLine($"Local time: {localTime}");
Verzögerte Operationen testen
FakeTimeProvider ist besonders nützlich für Testvorgänge, die Verzögerungen erfordern:
public class DelayedOperationTests
{
[Fact]
public async Task DelayedOperation_CompletesAfterDelay()
{
// Arrange
var fakeTimeProvider = new FakeTimeProvider();
var operation = new DelayedOperation(fakeTimeProvider);
// Act
Task task = operation.ExecuteAsync(TimeSpan.FromMinutes(5));
// Assert - operation should not be complete yet
Assert.False(task.IsCompleted);
// Advance time by 5 minutes
fakeTimeProvider.Advance(TimeSpan.FromMinutes(5));
// Wait for the task to complete
await task;
// Operation should now be complete
Assert.True(task.IsCompleted);
}
}
public class DelayedOperation(TimeProvider timeProvider)
{
public async Task ExecuteAsync(TimeSpan delay)
{
await Task.Delay(delay, timeProvider);
}
}
Regelmäßige Vorgänge testen
Testvorgänge, die in regelmäßigen Abständen mit Zeitgebern ausgeführt werden:
public class PeriodicOperationTests
{
[Fact]
public void PeriodicOperation_ExecutesAtIntervals()
{
// Arrange
var fakeTimeProvider = new FakeTimeProvider();
var counter = new PeriodicCounter(fakeTimeProvider);
counter.Start(TimeSpan.FromSeconds(10));
// Act & Assert
Assert.Equal(0, counter.Count);
// Advance by 10 seconds
fakeTimeProvider.Advance(TimeSpan.FromSeconds(10));
Assert.Equal(1, counter.Count);
// Advance by 20 more seconds
fakeTimeProvider.Advance(TimeSpan.FromSeconds(20));
Assert.Equal(3, counter.Count);
// Clean up
counter.Stop();
}
}
public class PeriodicCounter(TimeProvider timeProvider)
{
private ITimer? _timer;
public int Count { get; private set; }
public void Start(TimeSpan interval)
{
_timer = timeProvider.CreateTimer(
callback: _ => Count++,
state: null,
dueTime: interval,
period: interval);
}
public void Stop()
{
_timer?.Dispose();
}
}
Testen zeitbasierter Geschäftslogik
Testen Sie geschäftslogik, die von bestimmten Uhrzeiten oder Datumsangaben abhängt:
public class SubscriptionTests
{
[Fact]
public void Subscription_ExpiresAfterOneYear()
{
// Arrange
var fakeTimeProvider = new FakeTimeProvider();
var startDate = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
fakeTimeProvider.SetUtcNow(startDate);
var subscription = new Subscription(fakeTimeProvider);
subscription.Activate();
// Assert - subscription is active
Assert.True(subscription.IsActive);
// Act - advance time by 11 months
fakeTimeProvider.Advance(TimeSpan.FromDays(30 * 11));
Assert.True(subscription.IsActive);
// Advance time by 2 more months
fakeTimeProvider.Advance(TimeSpan.FromDays(60));
Assert.False(subscription.IsActive);
}
}
public class Subscription(TimeProvider timeProvider)
{
private DateTimeOffset _activationDate;
public void Activate()
{
_activationDate = timeProvider.GetUtcNow();
}
public bool IsActive
{
get
{
DateTimeOffset currentTime = timeProvider.GetUtcNow();
DateTimeOffset expirationDate = _activationDate.AddYears(1);
return currentTime < expirationDate;
}
}
}
Integration mit Abhängigkeitsinjektion
Verwendung FakeTimeProvider in Tests für Dienste, die mit Abhängigkeitsinjektion registriert sind:
public class CacheServiceTests
{
[Fact]
public void Cache_ExpiresAfterTimeout()
{
// Arrange
var fakeTimeProvider = new FakeTimeProvider();
var services = new ServiceCollection();
services.AddSingleton<TimeProvider>(fakeTimeProvider);
services.AddSingleton<CacheService>();
ServiceProvider provider = services.BuildServiceProvider();
CacheService cache = provider.GetRequiredService<CacheService>();
// Act
cache.Set("key", "value", TimeSpan.FromMinutes(10));
// Assert - value is present
Assert.True(cache.TryGet("key", out string? value));
Assert.Equal("value", value);
// Advance time beyond expiration
fakeTimeProvider.Advance(TimeSpan.FromMinutes(11));
// Value should be expired
Assert.False(cache.TryGet("key", out _));
}
}
public class CacheService(TimeProvider timeProvider)
{
private readonly Dictionary<string, CacheEntry> _cache = [];
public void Set(string key, string value, TimeSpan expiration)
{
DateTimeOffset expiresAt = timeProvider.GetUtcNow() + expiration;
_cache[key] = new CacheEntry(value, expiresAt);
}
public bool TryGet(string key, out string? value)
{
if (_cache.TryGetValue(key, out CacheEntry? entry))
{
if (timeProvider.GetUtcNow() < entry.ExpiresAt)
{
value = entry.Value;
return true;
}
// Entry expired, remove it
_cache.Remove(key);
}
value = null;
return false;
}
private record CacheEntry(string Value, DateTimeOffset ExpiresAt);
}
Bewährte Methoden
Berücksichtigen Sie bei der Verwendung FakeTimeProviderdie folgenden bewährten Methoden:
-
Inject TimeProvider: Verwenden Sie
TimeProviderimmer als Abhängigkeit, anstatt DateTime oder DateTimeOffset direkt zu verwenden. Dadurch kann Ihr Code getestet werden. - Verwenden Sie UTC-Zeit: Arbeiten Sie mit UTC-Zeit in Ihrer Geschäftslogik, und konvertieren Sie sie nur bei Bedarf für die Anzeige in ortsspezifische Zeit.
-
Testen Sie Randfälle: Verwenden Sie
FakeTimeProvider, um Randfälle wie Mitternacht, Monatsgrenzen, Sommerzeitübergänge und Schaltjahre zu testen. -
Bereinigen von Zeitgebern: Aufräumen von Zeitgebern, die mit
CreateTimererstellt wurden, um Ressourcenlecks in Ihren Tests zu vermeiden. - Zeit bewusst vorverlegen: Verlegen Sie in Ihren Tests die Zeit bewusst, um das Verhalten der Tests klar und vorhersehbar zu gestalten.
Vermeiden Sie das Mischen von realer und simulierten Zeit: Mischen Sie nicht reale Zeit mit im selben Test, da dies zu unvorhersehbarem Verhalten führen kann.