From 465c52eae42d30bdae22498f9440b8a6c5928638 Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Mon, 15 Jan 2024 23:49:31 -0800 Subject: [PATCH] Adding generator for generating extension method when IOptions is referenced --- Directory.Build.props | 2 +- Directory.Packages.props | 1 + .../ControllerWithOptionsTests.cs | 19 ++++++ .../ControllerWithOptionsTests.cs | 18 +++++ .../ControllerWithOptionsTests.cs | 18 +++++ .../ControllerWithOptions.cs | 12 ++++ .../Moq.AutoMock.Generator.Example.csproj | 6 +- .../TestsOptions.cs | 6 ++ .../OptionsExtensionSourceGenerator.cs | 66 +++++++++++++++++++ 9 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithOptionsTests.cs create mode 100644 GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithOptionsTests.cs create mode 100644 GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithOptionsTests.cs create mode 100644 GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithOptions.cs create mode 100644 GeneratorTests/Moq.AutoMocker.Generator.Example/TestsOptions.cs create mode 100644 Moq.AutoMocker.Generators/OptionsExtensionSourceGenerator.cs diff --git a/Directory.Build.props b/Directory.Build.props index 82dc6821..ac66fdbc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 10.0 + 11.0 enable enable true diff --git a/Directory.Packages.props b/Directory.Packages.props index 8e388a4f..30a331b4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,6 +7,7 @@ + diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithOptionsTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithOptionsTests.cs new file mode 100644 index 00000000..a60895fe --- /dev/null +++ b/GeneratorTests/Moq.AutoMock.Generator.Example.MSTest/ControllerWithOptionsTests.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Moq.AutoMock.Generator.Example.MSUnit; +[TestClass] +public class ControllerWithOptionsTests +{ + [TestMethod] + public void CreateInstance_WithOptions_EmbedsOptions() + { + AutoMocker mocker = new(); + + mocker.WithOptions(options => options.Number = 42); + + ControllerWithOptions controller = mocker.CreateInstance(); + + Assert.AreEqual(42, controller.Options.Value.Number); + } +} diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithOptionsTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithOptionsTests.cs new file mode 100644 index 00000000..c810af87 --- /dev/null +++ b/GeneratorTests/Moq.AutoMock.Generator.Example.NUnit/ControllerWithOptionsTests.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Options; +using NUnit.Framework; + +namespace Moq.AutoMock.Generator.Example.NUnit; +public class ControllerWithOptionsTests +{ + [Test] + public void CreateInstance_WithOptions_EmbedsOptions() + { + AutoMocker mocker = new(); + + mocker.WithOptions(options => options.Number = 42); + + ControllerWithOptions controller = mocker.CreateInstance(); + + Assert.That(42 == controller.Options.Value.Number); + } +} diff --git a/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithOptionsTests.cs b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithOptionsTests.cs new file mode 100644 index 00000000..d80c0318 --- /dev/null +++ b/GeneratorTests/Moq.AutoMock.Generator.Example.xUnit/ControllerWithOptionsTests.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Options; +using Xunit; + +namespace Moq.AutoMock.Generator.Example.xUnit; +public class ControllerWithOptionsTests +{ + [Fact] + public void CreateInstance_WithOptions_EmbedsOptions() + { + AutoMocker mocker = new(); + + mocker.WithOptions(options => options.Number = 42); + + ControllerWithOptions controller = mocker.CreateInstance(); + + Assert.Equal(42, controller.Options.Value.Number); + } +} diff --git a/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithOptions.cs b/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithOptions.cs new file mode 100644 index 00000000..318e788b --- /dev/null +++ b/GeneratorTests/Moq.AutoMocker.Generator.Example/ControllerWithOptions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Options; + +namespace Moq.AutoMock.Generator.Example; +public class ControllerWithOptions +{ + public IOptions Options { get; } + + public ControllerWithOptions(IOptions options) + { + Options = options; + } +} 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 268cbace..0858db35 100644 --- a/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj +++ b/GeneratorTests/Moq.AutoMocker.Generator.Example/Moq.AutoMock.Generator.Example.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -7,4 +7,8 @@ false + + + + diff --git a/GeneratorTests/Moq.AutoMocker.Generator.Example/TestsOptions.cs b/GeneratorTests/Moq.AutoMocker.Generator.Example/TestsOptions.cs new file mode 100644 index 00000000..f6d62da7 --- /dev/null +++ b/GeneratorTests/Moq.AutoMocker.Generator.Example/TestsOptions.cs @@ -0,0 +1,6 @@ +namespace Moq.AutoMock.Generator.Example; + +public class TestsOptions +{ + public int Number { get; set; } +} diff --git a/Moq.AutoMocker.Generators/OptionsExtensionSourceGenerator.cs b/Moq.AutoMocker.Generators/OptionsExtensionSourceGenerator.cs new file mode 100644 index 00000000..06e25ac8 --- /dev/null +++ b/Moq.AutoMocker.Generators/OptionsExtensionSourceGenerator.cs @@ -0,0 +1,66 @@ +using Microsoft.CodeAnalysis; + +namespace Moq.AutoMocker.Generators; + +[Generator] +public class OptionsExtensionSourceGenerator : ISourceGenerator +{ + public void Execute(GeneratorExecutionContext context) + { + if (!ReferencesOptions(context.Compilation.ReferencedAssemblyNames)) + { + return; + } + + context.AddSource("AutoMocker.Options.cs", OptionsExtensionContent); + } + + public void Initialize(GeneratorInitializationContext context) + { } + + private static bool ReferencesOptions(IEnumerable assemblies) + { + foreach (AssemblyIdentity assembly in assemblies) + { + if (assembly.Name.StartsWith("Microsoft.Extensions.Options")) + { + return true; + } + } + return false; + } + + private const string OptionsExtensionContent = + """ + namespace Moq.AutoMock + { + using Microsoft.Extensions.Options; + + public static class AutoMockerOptionsExtensions + { + /// + /// This method sets up with various option related services for Microsoft's Option pattern, and allows their interception and manipulation in testing scenarios. + /// + /// The instance + /// A delegate that can be used to configure an option instance of type TClass. + /// The type of Options being configured. + /// The same instance passed as parameter, allowing chained calls. + public static AutoMocker WithOptions(this AutoMocker mocker, Action? configure = null) + where TClass : class, new() + { + if (mocker == null) throw new ArgumentNullException(nameof(mocker)); + + mocker.Use>>(new[] { new ConfigureOptions(configure) }); + mocker.With, OptionsCache>(); + mocker.With, OptionsFactory>(); + mocker.With, OptionsMonitor>(); + mocker.With, OptionsManager>(); + TClass options = mocker.Get>().Create(string.Empty); + mocker.Use(Options.Create(options)); + mocker.Use(options); + return mocker; + } + } + } + """; +}