Skip to content

Commit f7ad0db

Browse files
committed
Adding generator for generating extension method when IOptions is referenced
1 parent 658b808 commit f7ad0db

File tree

9 files changed

+146
-2
lines changed

9 files changed

+146
-2
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22

33
<PropertyGroup>
4-
<LangVersion>10.0</LangVersion>
4+
<LangVersion>11.0</LangVersion>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<EnforceCodeStyleInBuild Condition=" '$(BuildingForLiveUnitTesting)' == '' ">true</EnforceCodeStyleInBuild>

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.3.0" />
88
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
99
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest" Version="1.1.1" />
10+
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.1" />
1011
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
1112
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
1213
<PackageVersion Include="Moq" Version="4.20.70" />
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.Extensions.Options;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
4+
namespace Moq.AutoMock.Generator.Example.MSUnit;
5+
[TestClass]
6+
public class ControllerWithOptionsTests
7+
{
8+
[TestMethod]
9+
public void CreateInstance_WithOptions_EmbedsOptions()
10+
{
11+
AutoMocker mocker = new();
12+
13+
mocker.WithOptions<TestsOptions>(options => options.Number = 42);
14+
15+
ControllerWithOptions controller = mocker.CreateInstance<ControllerWithOptions>();
16+
17+
Assert.AreEqual(42, controller.Options.Value.Number);
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Options;
2+
using NUnit.Framework;
3+
4+
namespace Moq.AutoMock.Generator.Example.NUnit;
5+
public class ControllerWithOptionsTests
6+
{
7+
[Test]
8+
public void CreateInstance_WithOptions_EmbedsOptions()
9+
{
10+
AutoMocker mocker = new();
11+
12+
mocker.WithOptions<TestsOptions>(options => options.Number = 42);
13+
14+
ControllerWithOptions controller = mocker.CreateInstance<ControllerWithOptions>();
15+
16+
Assert.That(42 == controller.Options.Value.Number);
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Options;
2+
using Xunit;
3+
4+
namespace Moq.AutoMock.Generator.Example.xUnit;
5+
public class ControllerWithOptionsTests
6+
{
7+
[Fact]
8+
public void CreateInstance_WithOptions_EmbedsOptions()
9+
{
10+
AutoMocker mocker = new();
11+
12+
mocker.WithOptions<TestsOptions>(options => options.Number = 42);
13+
14+
ControllerWithOptions controller = mocker.CreateInstance<ControllerWithOptions>();
15+
16+
Assert.Equal(42, controller.Options.Value.Number);
17+
}
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.Extensions.Options;
2+
3+
namespace Moq.AutoMock.Generator.Example;
4+
public class ControllerWithOptions
5+
{
6+
public IOptions<TestsOptions> Options { get; }
7+
8+
public ControllerWithOptions(IOptions<TestsOptions> options)
9+
{
10+
Options = options;
11+
}
12+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
@@ -7,4 +7,8 @@
77
<IsPackable>false</IsPackable>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.Extensions.Options" />
12+
</ItemGroup>
13+
1014
</Project>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Moq.AutoMock.Generator.Example;
2+
3+
public class TestsOptions
4+
{
5+
public int Number { get; set; }
6+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace Moq.AutoMocker.Generators;
4+
5+
[Generator]
6+
public class OptionsExtensionSourceGenerator : ISourceGenerator
7+
{
8+
public void Execute(GeneratorExecutionContext context)
9+
{
10+
if (!ReferencesOptions(context.Compilation.ReferencedAssemblyNames))
11+
{
12+
return;
13+
}
14+
15+
context.AddSource("AutoMocker.Options.cs", OptionsExtensionContent);
16+
}
17+
18+
public void Initialize(GeneratorInitializationContext context)
19+
{ }
20+
21+
private static bool ReferencesOptions(IEnumerable<AssemblyIdentity> assemblies)
22+
{
23+
foreach (AssemblyIdentity assembly in assemblies)
24+
{
25+
if (assembly.Name.StartsWith("Microsoft.Extensions.Options"))
26+
{
27+
return true;
28+
}
29+
}
30+
return false;
31+
}
32+
33+
private const string OptionsExtensionContent =
34+
"""
35+
namespace Moq.AutoMock
36+
{
37+
using Microsoft.Extensions.Options;
38+
39+
public static class AutoMockerOptionsExtensions
40+
{
41+
/// <summary>
42+
/// This method sets up <see cref="AutoMocker"/> with various option related services for Microsoft's Option pattern, and allows their interception and manipulation in testing scenarios.
43+
/// </summary>
44+
/// <param name="mocker">The <see cref="AutoMocker"/> instance</param>
45+
/// <param name="configure">A delegate that can be used to configure an option instance of type TClass.</param>
46+
/// <typeparam name="TClass">The type of Options being configured.</typeparam>
47+
/// <returns>The same <see cref="AutoMocker"/> instance passed as parameter, allowing chained calls.</returns>
48+
public static AutoMocker WithOptions<TClass>(this AutoMocker mocker, Action<TClass>? configure = null)
49+
where TClass : class, new()
50+
{
51+
if (mocker == null) throw new ArgumentNullException(nameof(mocker));
52+
53+
mocker.Use<IEnumerable<IConfigureOptions<TClass>>>(new[] { new ConfigureOptions<TClass>(configure) });
54+
mocker.With<IOptionsMonitorCache<TClass>, OptionsCache<TClass>>();
55+
mocker.With<IOptionsFactory<TClass>, OptionsFactory<TClass>>();
56+
mocker.With<IOptionsMonitor<TClass>, OptionsMonitor<TClass>>();
57+
mocker.With<IOptionsSnapshot<TClass>, OptionsManager<TClass>>();
58+
TClass options = mocker.Get<IOptionsFactory<TClass>>().Create(string.Empty);
59+
mocker.Use(Options.Create(options));
60+
mocker.Use(options);
61+
return mocker;
62+
}
63+
}
64+
}
65+
""";
66+
}

0 commit comments

Comments
 (0)