SweetMock.Extensions.TimeProvider
0.9.41
dotnet add package SweetMock.Extensions.TimeProvider --version 0.9.41
NuGet\Install-Package SweetMock.Extensions.TimeProvider -Version 0.9.41
<PackageReference Include="SweetMock.Extensions.TimeProvider" Version="0.9.41" />
<PackageVersion Include="SweetMock.Extensions.TimeProvider" Version="0.9.41" />
<PackageReference Include="SweetMock.Extensions.TimeProvider" />
paket add SweetMock.Extensions.TimeProvider --version 0.9.41
#r "nuget: SweetMock.Extensions.TimeProvider, 0.9.41"
#:package SweetMock.Extensions.TimeProvider@0.9.41
#addin nuget:?package=SweetMock.Extensions.TimeProvider&version=0.9.41
#tool nuget:?package=SweetMock.Extensions.TimeProvider&version=0.9.41
SweetMock.Extensions.TimeProvider
A lightweight extension library for SweetMock for mocking TimeProvider in .NET tests. Control time precisely in your tests without complex setup.
Features
- Mock TimeProvider: Full control over time in tests using
FakeTimeProvider - Auto-initialization: Automatically initialized to current UTC time — no setup required
- Set a Fixed Time: Initialize with a specific
DateTimeOffsetorFakeTimeProvider - Advance Time: Move time forward during a test with
Modify - Method Chaining: Chain multiple
Modifycalls for composable time manipulation - Timer Support: Test
ITimer-based logic by advancing time to trigger callbacks - Re-initialization: Reset the clock to a different time mid-test
Installation
dotnet add package SweetMock.Extensions.TimeProvider
Quick Start
Using with Fixtures
[Fixture<MyService>]
public class MyServiceTests
{
[Fact]
public void DefaultTimeProvider_UsesCurrentUtcTime()
{
// Arrange - no config needed, auto-initialized to UtcNow
var fixture = Fixture.MyService();
var sut = fixture.CreateMyService();
// Act
var result = sut.GetCurrentTime();
// Assert
Assert.True(DateTimeOffset.UtcNow - result < TimeSpan.FromMilliseconds(10));
}
[Fact]
public void TimeProvider_CanBeSetToAFixedTime()
{
// Arrange
var startTime = new DateTimeOffset(2025, 4, 15, 13, 51, 0, TimeSpan.Zero);
var fixture = Fixture.MyService(config =>
config.provider.Initialize(startTime)
);
var sut = fixture.CreateMyService();
// Act
var result = sut.GetCurrentTime();
// Assert
Assert.Equal(startTime, result);
}
}
public class MyService(TimeProvider provider)
{
public DateTimeOffset GetCurrentTime() => provider.GetUtcNow();
}
Initializing the Time Provider
Default Behavior
Without any configuration, the mock is automatically initialized to the current UTC time at the moment of fixture creation:
var fixture = Fixture.MyService();
var sut = fixture.CreateMyService();
// Time will be very close to DateTimeOffset.UtcNow
var result = sut.GetCurrentTime();
Initialize with a DateTimeOffset
var startTime = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
var fixture = Fixture.MyService(config =>
config.provider.Initialize(startTime)
);
Initialize with a FakeTimeProvider
var fakeTimeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 4, 15, 13, 51, 0, TimeSpan.Zero));
var fixture = Fixture.MyService(config =>
config.provider.Initialize(fakeTimeProvider)
);
Advancing Time
Use Modify to manipulate the FakeTimeProvider during a test. The Modify method requires the mock to have been initialized with a FakeTimeProvider (either directly or via Initialize).
[Fact]
public void TimeCanBeAdvancedDuringATest()
{
// Arrange
var startTime = new DateTimeOffset(2025, 4, 15, 13, 51, 0, TimeSpan.Zero);
var fixture = Fixture.MyService(config =>
config.provider.Initialize(startTime)
);
var sut = fixture.CreateMyService();
// Act
fixture.Config.provider.Modify(t => t.Advance(TimeSpan.FromMinutes(1)));
var result = sut.GetCurrentTime();
// Assert
Assert.Equal(new DateTimeOffset(2025, 4, 15, 13, 52, 0, TimeSpan.Zero), result);
}
Method Chaining
Multiple Modify calls are applied cumulatively and can be chained:
fixture.Config.provider
.Modify(t => t.Advance(TimeSpan.FromHours(1)))
.Modify(t => t.Advance(TimeSpan.FromMinutes(30)));
Re-initializing the Clock
You can reset the clock to a completely different time mid-test:
[Fact]
public void TimeProvider_CanBeReInitialized()
{
// Arrange
var firstTime = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
var secondTime = new DateTimeOffset(2030, 6, 15, 12, 0, 0, TimeSpan.Zero);
var fixture = Fixture.MyService(config =>
config.provider.Initialize(firstTime)
);
var sut = fixture.CreateMyService();
Assert.Equal(firstTime, sut.GetCurrentTime());
// Act
fixture.Config.provider.Initialize(secondTime);
// Assert
Assert.Equal(secondTime, sut.GetCurrentTime());
}
Timer Support
Advancing time with Modify also triggers any ITimer callbacks registered on the mock:
[Fact]
public void Timers_AreTriggeredByAdvancingTime()
{
// Arrange
var triggers = new List<string>();
var sut = Mock.TimeProvider(out var config);
// Act
var timer = sut.CreateTimer(
state => triggers.Add(state!.ToString()!),
"tick",
dueTime: TimeSpan.FromSeconds(1),
period: TimeSpan.FromSeconds(2)
);
config.Modify(t => t.Advance(TimeSpan.FromSeconds(7)));
// Assert - fires at 1s, 3s, 5s, 7s
Assert.Equal(4, triggers.Count);
}
Complete Example
[Fixture<SubscriptionService>]
[Mock<TimeProvider, MockOf_TimeProvider>]
public class SubscriptionServiceTests
{
[Fact]
public void Subscription_ExpiresAfterThirtyDays()
{
// Arrange
var startTime = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
var fixture = Fixture.SubscriptionService(config =>
config.provider.Initialize(startTime)
);
var sut = fixture.CreateSubscriptionService();
sut.Activate();
// Advance time just before expiry
fixture.Config.provider.Modify(t => t.Advance(TimeSpan.FromDays(29)));
Assert.True(sut.IsActive());
// Advance past expiry
fixture.Config.provider.Modify(t => t.Advance(TimeSpan.FromDays(2)));
Assert.False(sut.IsActive());
}
}
public class SubscriptionService(TimeProvider provider)
{
private DateTimeOffset? _activatedAt;
public void Activate() => _activatedAt = provider.GetUtcNow();
public bool IsActive() =>
_activatedAt.HasValue &&
provider.GetUtcNow() - _activatedAt.Value < TimeSpan.FromDays(30);
}
API Reference
MockOf_TimeProvider
The main mock class. Extends FakeTimeProviderMockBase and wraps FakeTimeProvider from Microsoft.Extensions.Time.Testing.
// Create directly
var sut = Mock.TimeProvider();
// Create with config
var sut = Mock.TimeProvider(out var config);
// Create with explicit config action
var sut = Mock.TimeProvider(config => config.Initialize(startTime));
MockConfig Methods
// Initialize from a DateTimeOffset
config.Initialize(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
// Initialize from a FakeTimeProvider
config.Initialize(new FakeTimeProvider(startTime));
// Initialize from any TimeProvider (copies all settings)
config.Initialize(myTimeProvider);
// Manipulate the underlying FakeTimeProvider
config.Modify(t => t.Advance(TimeSpan.FromSeconds(30)));
config.Modify(t => t.SetLocalTimeZone(TimeZoneInfo.Utc));
// Chain multiple modifications
config.Modify(t => t.Advance(TimeSpan.FromHours(1)))
.Modify(t => t.Advance(TimeSpan.FromMinutes(30)));
Note:
Modifyrequires the mock to have been initialized with aFakeTimeProvider. Calling it on a mock initialized via a plainTimeProviderwill throwNullReferenceException.
Requirements
- .NET 8.0, 9.0, or 10.0
- SweetMock
- Microsoft.Extensions.TimeProvider.Testing
License
See the main SweetMock repository for license information.
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 0.9.41)
-
net8.0
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 0.9.41)
-
net9.0
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 0.9.41)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.