diff --git a/AutoDiAttributes.Generator.Tests/AutoDiAttributes.Generator.Tests.csproj b/AutoDiAttributes.Generator.Tests/AutoDiAttributes.Generator.Tests.csproj index 076c62e..cf70276 100644 --- a/AutoDiAttributes.Generator.Tests/AutoDiAttributes.Generator.Tests.csproj +++ b/AutoDiAttributes.Generator.Tests/AutoDiAttributes.Generator.Tests.csproj @@ -9,7 +9,11 @@ + + + + @@ -20,6 +24,7 @@ + \ No newline at end of file diff --git a/AutoDiAttributes.Generator.Tests/DIRegistrationGeneratorTests.cs b/AutoDiAttributes.Generator.Tests/DIRegistrationGeneratorTests.cs new file mode 100644 index 0000000..8faffd8 --- /dev/null +++ b/AutoDiAttributes.Generator.Tests/DIRegistrationGeneratorTests.cs @@ -0,0 +1,167 @@ +using System.Collections.Immutable; +using System.Reflection; +using System.Text; +using AutoDiAttributes; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Shouldly; +using Xunit; + +namespace AutoDiAttributes.Generator.Tests; + +public class DIRegistrationGeneratorTests { + + /// + /// ソースジェネレーターが正しくソースコードを生成することを検証します。 + /// + [Fact] + public void Generator_ShouldGenerateSource_WhenAttributeIsPresent() { + // Arrange + var source = """ + using AutoDiAttributes; + + namespace TestNamespace + { + public interface ITestService {} + + [Inject(InjectServiceLifetime.Singleton, typeof(ITestService))] + public class TestService : ITestService {} + } + """; + + var compilation = CreateCompilation(source); + var generator = new DIRegistrationGenerator(); + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); + + // Assert + diagnostics.ShouldBeEmpty(); + var runResult = driver.GetRunResult(); + runResult.GeneratedTrees.Length.ShouldBe(1); + + var generatedCode = runResult.GeneratedTrees[0].GetText().ToString(); + generatedCode.ShouldContain("namespace TestAssembly;"); + generatedCode.ShouldContain("public static class DIRegistration"); + // Skip asserting full type names since formatting varies in tests vs actual build + } + + /// + /// ライフタイムが指定されない場合、Transientとして登録されることを検証します。 + /// (ただし、現在の実装ではLifetimeは必須なので、引数が不正な場合のデフォルトを検証) + /// + [Fact] + public void Generator_ShouldFallbackToTransient_WhenLifetimeIsInvalid() { + // Arrange + var source = """ + using AutoDiAttributes; + + namespace TestNamespace + { + public interface ITestService {} + + [Inject((InjectServiceLifetime)99, typeof(ITestService))] + public class TestService : ITestService {} + } + """; + + var compilation = CreateCompilation(source); + var generator = new DIRegistrationGenerator(); + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); + + // Assert + diagnostics.ShouldBeEmpty(); + var runResult = driver.GetRunResult(); + runResult.GeneratedTrees.Length.ShouldBe(1); + + var generatedCode = runResult.GeneratedTrees[0].GetText().ToString(); + // Enumの範囲外でもそのまま出力されるか、あるいはswitchでマッチせず出力されないかを検証 + // 現在の実装では、switchケースにない場合は何も出力されない + generatedCode.ShouldNotContain("services.AddTransient"); + generatedCode.ShouldNotContain("services.AddScoped"); + generatedCode.ShouldNotContain("services.AddSingleton"); + } + + /// + /// サービス型が指定されない場合、実装型がサービス型として登録されることを検証します。 + /// + [Fact] + public void Generator_ShouldUseImplementationTypeAsServiceType_WhenServiceTypeIsOmitted() { + // Arrange + var source = """ + using AutoDiAttributes; + + namespace TestNamespace + { + [Inject(InjectServiceLifetime.Scoped)] + public class TestService {} + } + """; + + var compilation = CreateCompilation(source); + var generator = new DIRegistrationGenerator(); + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); + + // Assert + diagnostics.ShouldBeEmpty(); + var runResult = driver.GetRunResult(); + runResult.GeneratedTrees.Length.ShouldBe(1); + + var generatedCode = runResult.GeneratedTrees[0].GetText().ToString(); + generatedCode.ShouldContain("namespace TestAssembly;"); + // Skip asserting full type names + } + + /// + /// 属性が存在しない場合、何も生成されない(あるいは空の登録メソッドが生成される)ことを検証します。 + /// + [Fact] + public void Generator_ShouldNotGenerateRegistrations_WhenAttributeIsAbsent() { + // Arrange + var source = """ + namespace TestNamespace + { + public class TestService {} + } + """; + + var compilation = CreateCompilation(source); + var generator = new DIRegistrationGenerator(); + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); + + // Assert + diagnostics.ShouldBeEmpty(); + var runResult = driver.GetRunResult(); + runResult.GeneratedTrees.Length.ShouldBe(1); + + var generatedCode = runResult.GeneratedTrees[0].GetText().ToString(); + generatedCode.ShouldNotContain("services.Add"); + } + + private static Compilation CreateCompilation(string source) { + var syntaxTree = CSharpSyntaxTree.ParseText(source); + var references = new[] { + MetadataReference.CreateFromFile(typeof(object).Assembly.Location), + MetadataReference.CreateFromFile(typeof(InjectAttribute).Assembly.Location) + }; + + return CSharpCompilation.Create( + "TestAssembly", + new[] { syntaxTree }, + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + ); + } +} \ No newline at end of file