Skip to content

Commit 3d27749

Browse files
committed
feat: Add Fake Time Provider extension generator
Introduces a source generator that creates a `WithFakeTimeProvider()` extension method for `AutoMocker`. This streamlines deterministic testing of time-sensitive components by automatically registering `FakeTimeProvider` from `Microsoft.Extensions.TimeProvider.Testing`. It registers the fake provider as both `TimeProvider` (for injection) and `FakeTimeProvider` (for test control). Includes new documentation and example tests across various test frameworks.
1 parent a9660f3 commit 3d27749

20 files changed

Lines changed: 666 additions & 1 deletion

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
2424
<PackageVersion Include="OpenTelemetry.Api" Version="1.15.3" />
2525
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.3" />
26+
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.0.0" />
2627
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="10.0.8" />
2728
<PackageVersion Include="System.Formats.Asn1" Version="10.0.8" />
2829
<PackageVersion Include="TUnit" Version="1.46.0" />

GeneratorTests/Moq.AutoMock.Generator.DisableTests/GeneratorDisableTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ public void ApplicationInsightsGenerator_WhenDisabled_ExtensionMethodDoesNotExis
5959
"WithApplicationInsights extension method should not exist when EnableMoqAutoMockerApplicationInsightsGenerator=false");
6060
}
6161

62+
[Fact]
63+
public void FakeTimeProviderGenerator_WhenDisabled_ExtensionMethodDoesNotExist()
64+
{
65+
// When EnableMoqAutoMockerFakeTimeProviderGenerator=false, the fake time provider
66+
// extension methods should not be generated
67+
68+
bool hasGeneratedTimeProviderExtension = HasExtensionMethod("WithFakeTimeProvider");
69+
Assert.False(hasGeneratedTimeProviderExtension,
70+
"WithFakeTimeProvider extension method should not exist when EnableMoqAutoMockerFakeTimeProviderGenerator=false");
71+
}
72+
6273
/// <summary>
6374
/// Checks if an extension method with the given name exists for the AutoMocker type
6475
/// in the current assembly (generated code is added to the consuming project).

GeneratorTests/Moq.AutoMock.Generator.DisableTests/Moq.AutoMock.Generator.DisableTests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<EnableMoqAutoMockerKeyedServicesGenerator>false</EnableMoqAutoMockerKeyedServicesGenerator>
1313
<EnableMoqAutoMockerFakeLoggingGenerator>false</EnableMoqAutoMockerFakeLoggingGenerator>
1414
<EnableMoqAutoMockerApplicationInsightsGenerator>false</EnableMoqAutoMockerApplicationInsightsGenerator>
15+
<EnableMoqAutoMockerFakeTimeProviderGenerator>false</EnableMoqAutoMockerFakeTimeProviderGenerator>
1516
</PropertyGroup>
1617

1718
<ItemGroup>
@@ -20,6 +21,7 @@
2021
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
2122
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
2223
<PackageReference Include="Microsoft.ApplicationInsights" />
24+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
2325
<PackageReference Include="xunit.v3" />
2426
<PackageReference Include="xunit.runner.visualstudio">
2527
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -56,6 +58,7 @@
5658
<CompilerVisibleProperty Include="EnableMoqAutoMockerKeyedServicesGenerator" />
5759
<CompilerVisibleProperty Include="EnableMoqAutoMockerFakeLoggingGenerator" />
5860
<CompilerVisibleProperty Include="EnableMoqAutoMockerApplicationInsightsGenerator" />
61+
<CompilerVisibleProperty Include="EnableMoqAutoMockerFakeTimeProviderGenerator" />
5962
</ItemGroup>
6063

6164
</Project>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Microsoft.Extensions.Time.Testing;
2+
3+
namespace Moq.AutoMock.Generator.Example.MSTest;
4+
5+
[TestClass]
6+
public class ControllerWithTimeProviderTests
7+
{
8+
[TestMethod]
9+
public void CreateInstance_WithFakeTimeProvider_CreatesController()
10+
{
11+
AutoMocker mocker = new();
12+
13+
mocker.WithFakeTimeProvider();
14+
15+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
16+
17+
Assert.IsNotNull(controller);
18+
}
19+
20+
[TestMethod]
21+
public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
22+
{
23+
AutoMocker mocker = new();
24+
25+
mocker.WithFakeTimeProvider();
26+
27+
var fakeTimeProvider = mocker.Get<FakeTimeProvider>();
28+
var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
29+
fakeTimeProvider.SetUtcNow(startTime);
30+
31+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
32+
33+
Assert.AreEqual(startTime, controller.GetCurrentTime());
34+
35+
fakeTimeProvider.Advance(TimeSpan.FromHours(1));
36+
37+
Assert.AreEqual(startTime.AddHours(1), controller.GetCurrentTime());
38+
}
39+
}

GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/Moq.AutoMock.Generator.Example.MSTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1313
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" />
14+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
1415
<PackageReference Include="MSTest" />
1516
<PackageReference Include="coverlet.collector">
1617
<PrivateAssets>all</PrivateAssets>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Microsoft.Extensions.Time.Testing;
2+
using NUnit.Framework;
3+
4+
namespace Moq.AutoMock.Generator.Example.NUnit;
5+
6+
public class ControllerWithTimeProviderTests
7+
{
8+
[Test]
9+
public void CreateInstance_WithFakeTimeProvider_CreatesController()
10+
{
11+
AutoMocker mocker = new();
12+
13+
mocker.WithFakeTimeProvider();
14+
15+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
16+
17+
Assert.That(controller, Is.Not.Null);
18+
}
19+
20+
[Test]
21+
public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
22+
{
23+
AutoMocker mocker = new();
24+
25+
mocker.WithFakeTimeProvider();
26+
27+
var fakeTimeProvider = mocker.Get<FakeTimeProvider>();
28+
var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
29+
fakeTimeProvider.SetUtcNow(startTime);
30+
31+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
32+
33+
Assert.That(controller.GetCurrentTime(), Is.EqualTo(startTime));
34+
35+
fakeTimeProvider.Advance(TimeSpan.FromHours(1));
36+
37+
Assert.That(controller.GetCurrentTime(), Is.EqualTo(startTime.AddHours(1)));
38+
}
39+
}

GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/Moq.AutoMock.Generator.Example.NUnit.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1313
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" />
14+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
1415
<PackageReference Include="NUnit" />
1516
<PackageReference Include="NUnit3TestAdapter" />
1617
<PackageReference Include="coverlet.collector">
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.Extensions.Time.Testing;
2+
3+
namespace Moq.AutoMock.Generator.Example.TUnit;
4+
5+
public partial class ControllerWithTimeProviderTests
6+
{
7+
[Test]
8+
public async Task CreateInstance_WithFakeTimeProvider_CreatesController()
9+
{
10+
AutoMocker mocker = new();
11+
12+
mocker.WithFakeTimeProvider();
13+
14+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
15+
16+
await Assert.That(controller).IsNotNull();
17+
}
18+
19+
[Test]
20+
public async Task CreateInstance_WithFakeTimeProvider_UsesControlledTime()
21+
{
22+
AutoMocker mocker = new();
23+
24+
mocker.WithFakeTimeProvider();
25+
26+
var fakeTimeProvider = mocker.Get<FakeTimeProvider>();
27+
var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
28+
fakeTimeProvider.SetUtcNow(startTime);
29+
30+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
31+
32+
await Assert.That(controller.GetCurrentTime()).IsEqualTo(startTime);
33+
34+
fakeTimeProvider.Advance(TimeSpan.FromHours(1));
35+
36+
await Assert.That(controller.GetCurrentTime()).IsEqualTo(startTime.AddHours(1));
37+
}
38+
}

GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/Moq.AutoMock.Generator.Example.TUnit.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<PackageReference Include="TUnit" />
1313
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" />
14+
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
1415
</ItemGroup>
1516

1617
<ItemGroup>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Microsoft.Extensions.Time.Testing;
2+
using Xunit;
3+
4+
namespace Moq.AutoMock.Generator.Example.xUnit;
5+
6+
public class ControllerWithTimeProviderTests
7+
{
8+
[Fact]
9+
public void CreateInstance_WithFakeTimeProvider_CreatesController()
10+
{
11+
AutoMocker mocker = new();
12+
13+
mocker.WithFakeTimeProvider();
14+
15+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
16+
17+
Assert.NotNull(controller);
18+
}
19+
20+
[Fact]
21+
public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
22+
{
23+
AutoMocker mocker = new();
24+
25+
mocker.WithFakeTimeProvider();
26+
27+
var fakeTimeProvider = mocker.Get<FakeTimeProvider>();
28+
var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
29+
fakeTimeProvider.SetUtcNow(startTime);
30+
31+
ControllerWithTimeProvider controller = mocker.CreateInstance<ControllerWithTimeProvider>();
32+
33+
Assert.Equal(startTime, controller.GetCurrentTime());
34+
35+
fakeTimeProvider.Advance(TimeSpan.FromHours(1));
36+
37+
Assert.Equal(startTime.AddHours(1), controller.GetCurrentTime());
38+
}
39+
}

0 commit comments

Comments
 (0)