diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs index 38fb99fadc..1d6e6cc0bb 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs @@ -6,6 +6,37 @@ namespace TUnit.Core.SourceGenerator.CodeGenerators.Helpers; public static class InstanceFactoryGenerator { + /// + /// Generates code to create an instance of a type with proper required property handling. + /// This handles required properties that don't have data sources by initializing them with defaults. + /// + public static void GenerateInstanceCreation(CodeWriter writer, ITypeSymbol typeSymbol, string variableName) + { + var className = typeSymbol.GloballyQualified(); + // Use GetAllRequiredProperties because even properties with data sources need to be + // initialized in the object initializer to satisfy C#'s required modifier constraint. + // The actual values will be populated by the data sources at runtime. + var requiredProperties = RequiredPropertyHelper.GetAllRequiredProperties(typeSymbol).ToArray(); + + if (requiredProperties.Length == 0) + { + writer.AppendLine($"{variableName} = new {className}();"); + } + else + { + writer.AppendLine($"{variableName} = new {className}()"); + writer.AppendLine("{"); + writer.Indent(); + foreach (var property in requiredProperties) + { + var defaultValue = RequiredPropertyHelper.GetDefaultValueForType(property.Type); + writer.AppendLine($"{property.Name} = {defaultValue},"); + } + writer.Unindent(); + writer.AppendLine("};"); + } + } + public static void GenerateInstanceFactory(CodeWriter writer, ITypeSymbol typeSymbol) { GenerateInstanceFactory(writer, typeSymbol, null); diff --git a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs index 0f232f71d3..8c5f623eb9 100644 --- a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs +++ b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs @@ -1048,7 +1048,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};"); @@ -1080,7 +1080,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};"); @@ -1124,7 +1124,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};"); @@ -1167,7 +1167,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};"); @@ -1217,7 +1217,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};"); @@ -1249,7 +1249,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};"); @@ -1293,7 +1293,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};"); @@ -1336,7 +1336,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper writer.AppendLine("else"); writer.AppendLine("{"); writer.Indent(); - writer.AppendLine($"instance = new {fullyQualifiedType}();"); + InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance"); writer.Unindent(); writer.AppendLine("}"); writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};"); diff --git a/TUnit.TestProject/Bugs/3951/Tests.cs b/TUnit.TestProject/Bugs/3951/Tests.cs new file mode 100644 index 0000000000..8527375a81 --- /dev/null +++ b/TUnit.TestProject/Bugs/3951/Tests.cs @@ -0,0 +1,48 @@ +using TUnit.Core.Interfaces; +using TUnit.TestProject.Attributes; + +namespace TUnit.TestProject.Bugs._3951; + + +public sealed class MyTestGeneratorAttribute : DataSourceGeneratorAttribute where T : MyType, new() +{ + protected override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) => [() => new T()]; +} + +public class MyType; + +public class ErrContext: IAsyncInitializer, IAsyncDisposable +{ + public ValueTask DisposeAsync() => default; + public Task InitializeAsync() => Task.CompletedTask; +} + +public class ErrFixture : IAsyncDisposable, IAsyncInitializer +{ + public required ErrContext Fixture { get; set; } + public ValueTask DisposeAsync() => default; + public Task InitializeAsync() => Task.CompletedTask; +} + +public class ErrTest +{ + [ClassDataSource>(Shared = SharedType.PerClass)] + public required ErrFixture Fixture { get; init; } + + public IEnumerable> TestExecutions => [() => Fixture.Fixture]; + + [MethodDataSource("TestExecutions")] + + [Test] + public async Task MyTest(ErrContext context) + { + await Assert.That(context.GetType()).IsNotAssignableTo(); + } + + [MyTestGeneratorAttribute] + [Test] + public async Task MyTest2(MyType t) + { + await Assert.That(t.GetType()).IsAssignableTo(); + } +}