diff --git a/Directory.Packages.props b/Directory.Packages.props
index 7754e07..60f2432 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -23,6 +23,7 @@
+
diff --git a/GeneratorTests/Moq.AutoMock.Generator.DisableTests/GeneratorDisableTests.cs b/GeneratorTests/Moq.AutoMock.Generator.DisableTests/GeneratorDisableTests.cs
index aef3c6e..c263d8a 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.DisableTests/GeneratorDisableTests.cs
+++ b/GeneratorTests/Moq.AutoMock.Generator.DisableTests/GeneratorDisableTests.cs
@@ -59,6 +59,17 @@ public void ApplicationInsightsGenerator_WhenDisabled_ExtensionMethodDoesNotExis
"WithApplicationInsights extension method should not exist when EnableMoqAutoMockerApplicationInsightsGenerator=false");
}
+ [Fact]
+ public void FakeTimeProviderGenerator_WhenDisabled_ExtensionMethodDoesNotExist()
+ {
+ // When EnableMoqAutoMockerFakeTimeProviderGenerator=false, the fake time provider
+ // extension methods should not be generated
+
+ bool hasGeneratedTimeProviderExtension = HasExtensionMethod("WithFakeTimeProvider");
+ Assert.False(hasGeneratedTimeProviderExtension,
+ "WithFakeTimeProvider extension method should not exist when EnableMoqAutoMockerFakeTimeProviderGenerator=false");
+ }
+
///
/// Checks if an extension method with the given name exists for the AutoMocker type
/// in the current assembly (generated code is added to the consuming project).
diff --git a/GeneratorTests/Moq.AutoMock.Generator.DisableTests/Moq.AutoMock.Generator.DisableTests.csproj b/GeneratorTests/Moq.AutoMock.Generator.DisableTests/Moq.AutoMock.Generator.DisableTests.csproj
index bcfc18d..c8e36ac 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.DisableTests/Moq.AutoMock.Generator.DisableTests.csproj
+++ b/GeneratorTests/Moq.AutoMock.Generator.DisableTests/Moq.AutoMock.Generator.DisableTests.csproj
@@ -12,6 +12,7 @@
false
false
false
+ false
@@ -20,6 +21,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -56,6 +58,7 @@
+
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithTimeProviderTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithTimeProviderTests.cs
new file mode 100644
index 0000000..fd77d81
--- /dev/null
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithTimeProviderTests.cs
@@ -0,0 +1,39 @@
+using Microsoft.Extensions.Time.Testing;
+
+namespace Moq.AutoMock.Generator.Example.MSTest;
+
+[TestClass]
+public class ControllerWithTimeProviderTests
+{
+ [TestMethod]
+ public void CreateInstance_WithFakeTimeProvider_CreatesController()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.IsNotNull(controller);
+ }
+
+ [TestMethod]
+ public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ var fakeTimeProvider = mocker.Get();
+ var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
+ fakeTimeProvider.SetUtcNow(startTime);
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.AreEqual(startTime, controller.GetCurrentTime());
+
+ fakeTimeProvider.Advance(TimeSpan.FromHours(1));
+
+ Assert.AreEqual(startTime.AddHours(1), controller.GetCurrentTime());
+ }
+}
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/Moq.AutoMock.Generator.Example.MSTest.csproj b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/Moq.AutoMock.Generator.Example.MSTest.csproj
index a391ec9..48417d9 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/Moq.AutoMock.Generator.Example.MSTest.csproj
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/Moq.AutoMock.Generator.Example.MSTest.csproj
@@ -11,6 +11,7 @@
+
all
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithTimeProviderTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithTimeProviderTests.cs
new file mode 100644
index 0000000..f1ac6fe
--- /dev/null
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithTimeProviderTests.cs
@@ -0,0 +1,39 @@
+using Microsoft.Extensions.Time.Testing;
+using NUnit.Framework;
+
+namespace Moq.AutoMock.Generator.Example.NUnit;
+
+public class ControllerWithTimeProviderTests
+{
+ [Test]
+ public void CreateInstance_WithFakeTimeProvider_CreatesController()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.That(controller, Is.Not.Null);
+ }
+
+ [Test]
+ public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ var fakeTimeProvider = mocker.Get();
+ var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
+ fakeTimeProvider.SetUtcNow(startTime);
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.That(controller.GetCurrentTime(), Is.EqualTo(startTime));
+
+ fakeTimeProvider.Advance(TimeSpan.FromHours(1));
+
+ Assert.That(controller.GetCurrentTime(), Is.EqualTo(startTime.AddHours(1)));
+ }
+}
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/Moq.AutoMock.Generator.Example.NUnit.csproj b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/Moq.AutoMock.Generator.Example.NUnit.csproj
index ac86626..31e88b1 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/Moq.AutoMock.Generator.Example.NUnit.csproj
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/Moq.AutoMock.Generator.Example.NUnit.csproj
@@ -11,6 +11,7 @@
+
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/ControllerWithTimeProviderTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/ControllerWithTimeProviderTests.cs
new file mode 100644
index 0000000..356a2d0
--- /dev/null
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/ControllerWithTimeProviderTests.cs
@@ -0,0 +1,38 @@
+using Microsoft.Extensions.Time.Testing;
+
+namespace Moq.AutoMock.Generator.Example.TUnit;
+
+public partial class ControllerWithTimeProviderTests
+{
+ [Test]
+ public async Task CreateInstance_WithFakeTimeProvider_CreatesController()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ await Assert.That(controller).IsNotNull();
+ }
+
+ [Test]
+ public async Task CreateInstance_WithFakeTimeProvider_UsesControlledTime()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ var fakeTimeProvider = mocker.Get();
+ var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
+ fakeTimeProvider.SetUtcNow(startTime);
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ await Assert.That(controller.GetCurrentTime()).IsEqualTo(startTime);
+
+ fakeTimeProvider.Advance(TimeSpan.FromHours(1));
+
+ await Assert.That(controller.GetCurrentTime()).IsEqualTo(startTime.AddHours(1));
+ }
+}
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/Moq.AutoMock.Generator.Example.TUnit.csproj b/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/Moq.AutoMock.Generator.Example.TUnit.csproj
index b27792d..e4a50a0 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/Moq.AutoMock.Generator.Example.TUnit.csproj
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.TUnit/Moq.AutoMock.Generator.Example.TUnit.csproj
@@ -11,6 +11,7 @@
+
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithTimeProviderTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithTimeProviderTests.cs
new file mode 100644
index 0000000..37d47dc
--- /dev/null
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithTimeProviderTests.cs
@@ -0,0 +1,39 @@
+using Microsoft.Extensions.Time.Testing;
+using Xunit;
+
+namespace Moq.AutoMock.Generator.Example.xUnit;
+
+public class ControllerWithTimeProviderTests
+{
+ [Fact]
+ public void CreateInstance_WithFakeTimeProvider_CreatesController()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.NotNull(controller);
+ }
+
+ [Fact]
+ public void CreateInstance_WithFakeTimeProvider_UsesControlledTime()
+ {
+ AutoMocker mocker = new();
+
+ mocker.WithFakeTimeProvider();
+
+ var fakeTimeProvider = mocker.Get();
+ var startTime = new DateTimeOffset(2025, 1, 1, 12, 0, 0, TimeSpan.Zero);
+ fakeTimeProvider.SetUtcNow(startTime);
+
+ ControllerWithTimeProvider controller = mocker.CreateInstance();
+
+ Assert.Equal(startTime, controller.GetCurrentTime());
+
+ fakeTimeProvider.Advance(TimeSpan.FromHours(1));
+
+ Assert.Equal(startTime.AddHours(1), controller.GetCurrentTime());
+ }
+}
diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/Moq.AutoMock.Generator.Example.xUnit.csproj b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/Moq.AutoMock.Generator.Example.xUnit.csproj
index 6c040c4..4828a9c 100644
--- a/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/Moq.AutoMock.Generator.Example.xUnit.csproj
+++ b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/Moq.AutoMock.Generator.Example.xUnit.csproj
@@ -11,6 +11,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithTimeProvider.cs b/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithTimeProvider.cs
new file mode 100644
index 0000000..2b7db73
--- /dev/null
+++ b/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithTimeProvider.cs
@@ -0,0 +1,13 @@
+namespace Moq.AutoMock.Generator.Example;
+
+public class ControllerWithTimeProvider
+{
+ private readonly TimeProvider _timeProvider;
+
+ public ControllerWithTimeProvider(TimeProvider timeProvider)
+ {
+ _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
+ }
+
+ public DateTimeOffset GetCurrentTime() => _timeProvider.GetUtcNow();
+}
diff --git a/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj b/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj
index 0cb908c..7635cba 100644
--- a/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj
+++ b/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj
@@ -10,6 +10,7 @@
+
diff --git a/Moq.AutoMock/build/Moq.AutoMock.props b/Moq.AutoMock/build/Moq.AutoMock.props
index 4198f60..5299ef8 100644
--- a/Moq.AutoMock/build/Moq.AutoMock.props
+++ b/Moq.AutoMock/build/Moq.AutoMock.props
@@ -9,5 +9,6 @@
+
diff --git a/Moq.AutoMocker.Generators.Tests/CSharpSourceGeneratorVerifier.cs b/Moq.AutoMocker.Generators.Tests/CSharpSourceGeneratorVerifier.cs
index 355648c..a67a28f 100644
--- a/Moq.AutoMocker.Generators.Tests/CSharpSourceGeneratorVerifier.cs
+++ b/Moq.AutoMocker.Generators.Tests/CSharpSourceGeneratorVerifier.cs
@@ -17,6 +17,7 @@ public class Test : CSharpSourceGeneratorTest GetSourceGenerators()
protected override Project ApplyCompilationOptions(Project project)
{
//project.AnalyzerOptions.WithAdditionalFiles();
- if (ReferenceAutoMocker || ReferenceOptionsAbstractions || ReferenceFakeLogging || ReferenceApplicationInsights || ReferenceDependencyInjection || ReferenceOpenTelemetryInMemoryExporter || ReferenceDiagnosticSource)
+ if (ReferenceAutoMocker || ReferenceOptionsAbstractions || ReferenceFakeLogging || ReferenceFakeTimeProvider || ReferenceApplicationInsights || ReferenceDependencyInjection || ReferenceOpenTelemetryInMemoryExporter || ReferenceDiagnosticSource)
{
string fullPath = Path.GetFullPath($"{AutoMock.AssemblyName}.dll");
project = project.AddMetadataReference(MetadataReference.CreateFromFile(fullPath));
@@ -80,6 +81,20 @@ protected override Project ApplyCompilationOptions(Project project)
}
}
+ if (ReferenceFakeTimeProvider)
+ {
+ // Add reference to Microsoft.Extensions.TimeProvider.Testing
+ try
+ {
+ var fakeTimeProviderAssembly = typeof(Microsoft.Extensions.Time.Testing.FakeTimeProvider).Assembly;
+ project = project.AddMetadataReference(MetadataReference.CreateFromFile(fakeTimeProviderAssembly.Location));
+ }
+ catch
+ {
+ // If we can't find the assembly, the test will fail, which is appropriate
+ }
+ }
+
if (ReferenceApplicationInsights)
{
// Add reference to Microsoft.ApplicationInsights
diff --git a/Moq.AutoMocker.Generators.Tests/FakeTimeProviderGeneratorTests.cs b/Moq.AutoMocker.Generators.Tests/FakeTimeProviderGeneratorTests.cs
new file mode 100644
index 0000000..2f8ced5
--- /dev/null
+++ b/Moq.AutoMocker.Generators.Tests/FakeTimeProviderGeneratorTests.cs
@@ -0,0 +1,116 @@
+using Microsoft.CodeAnalysis.Text;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using VerifyCS = Moq.AutoMocker.Generators.Tests.CSharpSourceGeneratorVerifier;
+using Microsoft.CodeAnalysis.Testing;
+
+namespace Moq.AutoMocker.Generators.Tests;
+
+[TestClass]
+public class FakeTimeProviderGeneratorTests
+{
+ private const string ExpectedFakeTimeProviderGeneratedFile = """
+ #nullable enable
+ // Generated by Moq.AutoMocker.Generators.FakeTimeProviderExtensionSourceGenerator.
+ // Licensed under the MIT License. See LICENSE in the project root for license information.
+ namespace Moq.AutoMock
+ {
+ using System;
+ using Microsoft.Extensions.Time.Testing;
+
+ ///
+ /// This class provides extension methods for interacting with in an instance.
+ ///
+ static partial class AutoMockerFakeTimeProviderExtensions
+ {
+ ///
+ /// This method sets up with a from Microsoft.Extensions.Time.Testing,
+ /// allowing time-dependent components to be tested in a deterministic way.
+ ///
+ /// The instance.
+ /// The same instance passed as parameter, allowing chained calls.
+ public static AutoMocker WithFakeTimeProvider(this AutoMocker mocker)
+ {
+ if (mocker == null)
+ {
+ throw new ArgumentNullException(nameof(mocker));
+ }
+
+ var timeProvider = new FakeTimeProvider();
+ mocker.Use(timeProvider);
+ mocker.Use(timeProvider);
+
+ return mocker;
+ }
+ }
+ }
+ """;
+
+ [TestMethod]
+ public async Task WhenTimeProviderAssemblyIsNotReferenced_NoGenerationOccurs()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode = "",
+ ReferenceFakeTimeProvider = false
+ }.RunAsync(TestContext.CancellationToken);
+ }
+
+ [TestMethod]
+ public async Task WhenTimeProviderAssemblyIsReferenced_ExtensionMethodIsGenerated()
+ {
+ await new VerifyCS.Test
+ {
+ CompilerDiagnostics = CompilerDiagnostics.None,
+ ReferenceFakeTimeProvider = true,
+ TestCode = "",
+ TestState =
+ {
+ GeneratedSources =
+ {
+ GetSourceFile(ExpectedFakeTimeProviderGeneratedFile, "AutoMockerFakeTimeProviderExtensions.g.cs")
+ }
+ }
+ }.RunAsync(TestContext.CancellationToken);
+ }
+
+ [TestMethod]
+ public async Task WhenGeneratorIsDisabled_NoGenerationOccurs()
+ {
+ var test = new VerifyCS.Test
+ {
+ ReferenceFakeTimeProvider = true,
+ TestCode = ""
+ };
+ test.SetGlobalOption("build_property.EnableMoqAutoMockerFakeTimeProviderGenerator", "false");
+ await test.RunAsync(TestContext.CancellationToken);
+ }
+
+ [TestMethod]
+ public async Task WhenGeneratorIsExplicitlyEnabled_ExtensionMethodIsGenerated()
+ {
+ var test = new VerifyCS.Test
+ {
+ CompilerDiagnostics = CompilerDiagnostics.None,
+ ReferenceFakeTimeProvider = true,
+ TestCode = "",
+ TestState =
+ {
+ GeneratedSources =
+ {
+ GetSourceFile(ExpectedFakeTimeProviderGeneratedFile, "AutoMockerFakeTimeProviderExtensions.g.cs")
+ }
+ }
+ };
+ test.SetGlobalOption("build_property.EnableMoqAutoMockerFakeTimeProviderGenerator", "true");
+ await test.RunAsync(TestContext.CancellationToken);
+ }
+
+ private static (string FileName, SourceText SourceText) GetSourceFile(string content, string fileName)
+ {
+ return (Path.Combine("Moq.AutoMocker.Generators", "Moq.AutoMocker.Generators.FakeTimeProviderExtensionSourceGenerator", fileName), SourceText.From(content, Encoding.UTF8, SourceHashAlgorithm.Sha256));
+ }
+
+ public TestContext TestContext { get; set; }
+}
diff --git a/Moq.AutoMocker.Generators.Tests/Moq.AutoMocker.Generators.Tests.csproj b/Moq.AutoMocker.Generators.Tests/Moq.AutoMocker.Generators.Tests.csproj
index fdc5ec2..5a04860 100644
--- a/Moq.AutoMocker.Generators.Tests/Moq.AutoMocker.Generators.Tests.csproj
+++ b/Moq.AutoMocker.Generators.Tests/Moq.AutoMocker.Generators.Tests.csproj
@@ -12,6 +12,7 @@
+
diff --git a/Moq.AutoMocker.Generators/FakeTimeProviderExtensionSourceGenerator.cs b/Moq.AutoMocker.Generators/FakeTimeProviderExtensionSourceGenerator.cs
new file mode 100644
index 0000000..b7e884f
--- /dev/null
+++ b/Moq.AutoMocker.Generators/FakeTimeProviderExtensionSourceGenerator.cs
@@ -0,0 +1,92 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Moq.AutoMocker.Generators;
+
+[Generator(LanguageNames.CSharp)]
+public sealed class FakeTimeProviderExtensionSourceGenerator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Check if the generator is enabled via MSBuild property
+ IncrementalValueProvider isEnabled = context.AnalyzerConfigOptionsProvider
+ .Select(static (provider, _) => IsGeneratorEnabled(provider));
+
+ // Check if Microsoft.Extensions.TimeProvider.Testing is referenced
+ IncrementalValueProvider referencesFakeTimeProvider = context.CompilationProvider
+ .Select(static (compilation, _) => ReferencesFakeTimeProvider(compilation.ReferencedAssemblyNames));
+
+ // Combine both conditions
+ IncrementalValueProvider shouldGenerate = isEnabled
+ .Combine(referencesFakeTimeProvider)
+ .Select(static (tuple, _) => tuple.Left && tuple.Right);
+
+ // Only generate source if enabled and Microsoft.Extensions.TimeProvider.Testing is referenced
+ context.RegisterSourceOutput(shouldGenerate, static (context, shouldGenerate) =>
+ {
+ if (shouldGenerate)
+ {
+ context.AddSource("AutoMockerFakeTimeProviderExtensions.g.cs", FakeTimeProviderExtensionContent);
+ }
+ });
+ }
+
+ private static bool IsGeneratorEnabled(AnalyzerConfigOptionsProvider provider)
+ {
+ if (provider.GlobalOptions.TryGetValue("build_property.EnableMoqAutoMockerFakeTimeProviderGenerator", out var value))
+ {
+ return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase);
+ }
+ return true; // Enabled by default
+ }
+
+ private static bool ReferencesFakeTimeProvider(IEnumerable assemblies)
+ {
+ foreach (AssemblyIdentity assembly in assemblies)
+ {
+ if (assembly.Name.Equals("Microsoft.Extensions.TimeProvider.Testing", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private const string FakeTimeProviderExtensionContent =
+ """
+ #nullable enable
+ // Generated by Moq.AutoMocker.Generators.FakeTimeProviderExtensionSourceGenerator.
+ // Licensed under the MIT License. See LICENSE in the project root for license information.
+ namespace Moq.AutoMock
+ {
+ using System;
+ using Microsoft.Extensions.Time.Testing;
+
+ ///
+ /// This class provides extension methods for interacting with in an instance.
+ ///
+ static partial class AutoMockerFakeTimeProviderExtensions
+ {
+ ///
+ /// This method sets up with a from Microsoft.Extensions.Time.Testing,
+ /// allowing time-dependent components to be tested in a deterministic way.
+ ///
+ /// The instance.
+ /// The same instance passed as parameter, allowing chained calls.
+ public static AutoMocker WithFakeTimeProvider(this AutoMocker mocker)
+ {
+ if (mocker == null)
+ {
+ throw new ArgumentNullException(nameof(mocker));
+ }
+
+ var timeProvider = new FakeTimeProvider();
+ mocker.Use(timeProvider);
+ mocker.Use(timeProvider);
+
+ return mocker;
+ }
+ }
+ }
+ """;
+}
diff --git a/docs/SourceGenerators.md b/docs/SourceGenerators.md
index 0eda19f..1f423e8 100644
--- a/docs/SourceGenerators.md
+++ b/docs/SourceGenerators.md
@@ -128,6 +128,28 @@ var service = mocker.CreateInstance();
[Learn more →](SourceGenerators/MeterFactoryExtensionGenerator.md)
+### 7. [Fake Time Provider Extension Generator](SourceGenerators/FakeTimeProviderExtensionGenerator.md)
+
+Generates `WithFakeTimeProvider()` extension method when `Microsoft.Extensions.TimeProvider.Testing` is referenced.
+
+**Key Features:**
+- Provides a `FakeTimeProvider` for deterministic time-based testing
+- Registers as both `TimeProvider` (for injection) and `FakeTimeProvider` (for test control)
+- Allows advancing time with `Advance()` and `SetUtcNow()`
+
+**Quick Example:**
+```csharp
+mocker.WithFakeTimeProvider();
+var fakeTime = mocker.Get();
+fakeTime.SetUtcNow(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
+
+var service = mocker.CreateInstance();
+fakeTime.Advance(TimeSpan.FromHours(1));
+// Verify behavior at specific time
+```
+
+[Learn more →](SourceGenerators/FakeTimeProviderExtensionGenerator.md)
+
## Important: Generated Classes Are Internal Partials
All extension classes created by these source generators are generated as **partial classes** with **internal visibility**. For example, the Keyed Services generator produces:
@@ -170,6 +192,7 @@ Each source generator can be individually disabled using MSBuild properties in y
| Fake Logging Extension | `EnableMoqAutoMockerFakeLoggingGenerator` |
| Application Insights Extension | `EnableMoqAutoMockerApplicationInsightsGenerator` |
| Meter Factory Extension | `EnableMoqAutoMockerMeterFactoryGenerator` |
+| Fake Time Provider Extension | `EnableMoqAutoMockerFakeTimeProviderGenerator` |
**Example: Disabling a generator**
```xml
diff --git a/docs/SourceGenerators/FakeTimeProviderExtensionGenerator.md b/docs/SourceGenerators/FakeTimeProviderExtensionGenerator.md
new file mode 100644
index 0000000..cb57a5d
--- /dev/null
+++ b/docs/SourceGenerators/FakeTimeProviderExtensionGenerator.md
@@ -0,0 +1,229 @@
+# Fake Time Provider Extension Generator
+
+When your test project references `Microsoft.Extensions.TimeProvider.Testing`, this generator creates a `WithFakeTimeProvider()` extension method for `AutoMocker` that sets up a `FakeTimeProvider` for deterministic time-based testing.
+
+## Features
+
+- Automatically generates when `Microsoft.Extensions.TimeProvider.Testing` is referenced
+- Registers `FakeTimeProvider` as both `TimeProvider` (for constructor injection) and `FakeTimeProvider` (for test control)
+- Enables deterministic time manipulation with `Advance()`, `SetUtcNow()`, and `AutoAdvanceAmount`
+- Uses Microsoft's official `FakeTimeProvider` from `Microsoft.Extensions.Time.Testing`
+
+## Installation
+
+Install the `Microsoft.Extensions.TimeProvider.Testing` package:
+
+```bash
+dotnet add package Microsoft.Extensions.TimeProvider.Testing
+```
+
+## Usage
+
+```csharp
+using Microsoft.Extensions.Time.Testing;
+
+public class SchedulerService
+{
+ private readonly TimeProvider _timeProvider;
+
+ public SchedulerService(TimeProvider timeProvider)
+ {
+ _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
+ }
+
+ public bool IsBusinessHours()
+ {
+ var now = _timeProvider.GetLocalNow();
+ return now.Hour >= 9 && now.Hour < 17;
+ }
+}
+
+[TestClass]
+public class SchedulerServiceTests
+{
+ [TestMethod]
+ public void IsBusinessHours_DuringBusinessDay_ReturnsTrue()
+ {
+ // Arrange
+ AutoMocker mocker = new();
+ mocker.WithFakeTimeProvider();
+
+ var fakeTime = mocker.Get();
+ fakeTime.SetUtcNow(new DateTimeOffset(2025, 1, 13, 14, 0, 0, TimeSpan.Zero)); // 2 PM UTC
+
+ // Act
+ SchedulerService service = mocker.CreateInstance();
+
+ // Assert
+ Assert.IsTrue(service.IsBusinessHours());
+ }
+
+ [TestMethod]
+ public void IsBusinessHours_AfterHours_ReturnsFalse()
+ {
+ // Arrange
+ AutoMocker mocker = new();
+ mocker.WithFakeTimeProvider();
+
+ var fakeTime = mocker.Get();
+ fakeTime.SetUtcNow(new DateTimeOffset(2025, 1, 13, 20, 0, 0, TimeSpan.Zero)); // 8 PM UTC
+
+ // Act
+ SchedulerService service = mocker.CreateInstance();
+
+ // Assert
+ Assert.IsFalse(service.IsBusinessHours());
+ }
+}
+```
+
+## Generated Extension Method
+
+The generator creates:
+
+```csharp
+public static AutoMocker WithFakeTimeProvider(this AutoMocker mocker)
+{
+ var timeProvider = new FakeTimeProvider();
+ mocker.Use(timeProvider);
+ mocker.Use(timeProvider);
+ return mocker;
+}
+```
+
+## How It Works
+
+The `WithFakeTimeProvider()` method:
+
+1. Creates a `FakeTimeProvider` instance
+2. Registers it as `TimeProvider` so classes with `TimeProvider` constructor parameters receive it automatically
+3. Registers it as `FakeTimeProvider` so tests can retrieve and control it via `mocker.Get()`
+
+Both registrations point to the same instance, ensuring that the time your test controls is the same time the service under test observes.
+
+## Advanced Usage
+
+### Advancing Time
+
+```csharp
+[TestMethod]
+public void Cache_AfterExpiry_RemovesEntry()
+{
+ // Arrange
+ AutoMocker mocker = new();
+ mocker.WithFakeTimeProvider();
+
+ var fakeTime = mocker.Get();
+ fakeTime.SetUtcNow(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
+
+ ExpiryCache cache = mocker.CreateInstance();
+ cache.Add("key", "value");
+
+ // Act — advance past expiry
+ fakeTime.Advance(TimeSpan.FromMinutes(10));
+
+ // Assert
+ Assert.IsFalse(cache.TryGetValue("key", out _));
+}
+```
+
+### Auto-Advancing Time
+
+`FakeTimeProvider` supports automatically advancing time on each `GetUtcNow()` call:
+
+```csharp
+[TestMethod]
+public void Service_WithAutoAdvance_ObservesPassingTime()
+{
+ AutoMocker mocker = new();
+ mocker.WithFakeTimeProvider();
+
+ var fakeTime = mocker.Get();
+ fakeTime.AutoAdvanceAmount = TimeSpan.FromSeconds(1);
+
+ TimestampService service = mocker.CreateInstance();
+
+ var first = service.GetTimestamp();
+ var second = service.GetTimestamp();
+
+ Assert.IsTrue(second > first);
+}
+```
+
+### Chaining with Other Extensions
+
+`WithFakeTimeProvider()` returns the `AutoMocker` instance for fluent chaining:
+
+```csharp
+AutoMocker mocker = new AutoMocker()
+ .WithFakeTimeProvider()
+ .WithFakeLogging();
+```
+
+## Disabling the Generator
+
+You can disable this generator using an MSBuild property in your test project's `.csproj` file:
+
+```xml
+
+ false
+
+```
+
+### Example: Disabling in Project File
+
+```xml
+
+
+ net8.0
+
+
+ false
+
+
+
+
+
+
+
+```
+
+## Troubleshooting
+
+### Extension Method Not Available
+
+1. Verify `Microsoft.Extensions.TimeProvider.Testing` is referenced in your test project
+2. Check that the generator is not disabled in your `.csproj`
+3. Rebuild the project to trigger generator execution
+4. Ensure you are using the `Moq.AutoMock` namespace
+
+### `TimeProvider` Not Resolved
+
+The `WithFakeTimeProvider()` call must happen before `CreateInstance()`:
+
+```csharp
+// ✅ Correct — configure before creating the instance
+mocker.WithFakeTimeProvider();
+var service = mocker.CreateInstance();
+
+// ❌ Incorrect — too late, the instance already has a mocked TimeProvider
+var service = mocker.CreateInstance();
+mocker.WithFakeTimeProvider();
+```
+
+### Accessing `FakeTimeProvider` for Time Control
+
+Use `mocker.Get()` to retrieve the instance registered by `WithFakeTimeProvider()`:
+
+```csharp
+mocker.WithFakeTimeProvider();
+var fakeTime = mocker.Get();
+fakeTime.Advance(TimeSpan.FromDays(1));
+```
+
+## See Also
+
+- [Source Generators Overview](../SourceGenerators.md)
+- [Fake Logging Extension Generator](FakeLoggingExtensionGenerator.md)
+- [Microsoft.Extensions.TimeProvider.Testing on NuGet](https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing)
+- [FakeTimeProvider API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.time.testing.faketimeprovider)