Skip to content

Generated test class fails "required member must be set in object initializer" #3951

@TheRumle

Description

@TheRumle

Info

  • IDE: JetBrains Rider 2025.2.4
  • TUnit version: 1.3.0 and also recreated on 1.2.11

Recreation of the problem

I have a compositional hierarchy equivalent to this the following classes:

public sealed class MyTestGeneratorAttribute<T> : DataSourceGeneratorAttribute<T> where T : MyType, new() 
{
    protected override IEnumerable<Func<T>> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) => [() => new T()];
}

public class MyType;

public class ErrContext: IAsyncInitializer, IAsyncDisposable
{
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
    public Task InitializeAsync() => Task.CompletedTask;
}

public class ErrFixture<T> : IAsyncDisposable, IAsyncInitializer
{ 
    public required ErrContext Fixture { get; set; }
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
    public Task InitializeAsync() => Task.CompletedTask;
}

public class ErrTest
{
    [ClassDataSource<ErrFixture<MyType>>(Shared = SharedType.PerClass)]
    public required ErrFixture<MyType> Fixture { get; init; }
    
    public IEnumerable<Func<ErrContext>> TestExecutions => [() => Fixture.Fixture];
    
    [MethodDataSource("TestExecutions")]

    [Test]
    public async Task MyTest(ErrContext context)
    {
        await Assert.That(context.GetType()).IsNotAssignableTo<MyType>();
    }
    
    [MyTestGeneratorAttribute<MyType>]
    [Test]
    public async Task MyTest2(MyType t)
    {
        await Assert.That(t.GetType()).IsAssignableTo<MyType>();
    }
}

This generates the following file that has compile time error:

// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
internal sealed class MyNamespace_ErrTest_MyFailingTestIsHere__ErrContext_TestSource : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
{
    #if NET8_0_OR_GREATER
    [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Field, Name = "<Fixture>k__BackingField")]
    private static extern ref global::MyNamespace.ErrFixture<global::MyNamespace.MyType> GetFixtureBackingField(global::MyNamespace.ErrTest instance);
    #endif
    public async global::System.Collections.Generic.IAsyncEnumerable<global::TUnit.Core.TestMetadata> GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
    {
        var metadata = new global::TUnit.Core.TestMetadata<global::MyNamespace.ErrTest>
        {
            TestName = "MyFailingTestIsHere",
            TestClassType = typeof(global::MyNamespace.ErrTest),
            TestMethodName = "MyFailingTestIsHere",
            Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
            AttributeFactory = static () =>
            [
                new global::TUnit.Core.TestAttribute(),
                new global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Design", "S3993")
{Justification = "TUnits' attributeusage must not and cannot be overwritten.",},
                new global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Maintainability", "S4144")
{Justification =             "This is a test class and method consuming code will not accidentally call wrong method due to similar signatures.",},
                new global::TUnit.Core.ParallelLimiterAttribute<global::Hoeyer.OpcUa.EndToEndTest.Configuration.ParallelLimit>(),
                new global::System.Reflection.AssemblyCompanyAttribute("Hoeyer.OpcUa.EndToEndTest"),
                new global::System.Reflection.AssemblyConfigurationAttribute("Debug"),
                new global::System.Reflection.AssemblyFileVersionAttribute("1.0.0.0"),
                new global::System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+6ce90c5365417e7937319199a18c15ee9dcf63c7"),
                new global::System.Reflection.AssemblyProductAttribute("Hoeyer.OpcUa.EndToEndTest"),
                new global::System.Reflection.AssemblyTitleAttribute("Hoeyer.OpcUa.EndToEndTest"),
                new global::System.Reflection.AssemblyVersionAttribute("1.0.0.0"),
                new global::System.Reflection.AssemblyMetadataAttribute("Microsoft.Testing.Platform.Application", "true")
            ],
            DataSources = new global::TUnit.Core.IDataSourceAttribute[]
            {
                new global::TUnit.Core.MethodDataSourceAttribute("TestExecutions")
                {
                    Factory = (dataGeneratorMetadata) =>
                    {
                        async global::System.Collections.Generic.IAsyncEnumerable<global::System.Func<global::System.Threading.Tasks.Task<object?[]?>>> Factory()
                        {
                            object? instance;
                            if (dataGeneratorMetadata.TestClassInstance != null)
                            {
                                instance = dataGeneratorMetadata.TestClassInstance;
                            }
                            else
                            {
                                instance = new global::MyNamespace.ErrTest();
                            }
                            var result = ((global::MyNamespace.ErrTest)instance).TestExecutions;
                            if (result is global::System.Collections.IEnumerable enumerable && !(result is string))
                            {
                                foreach (var item in enumerable)
                                {
                                    yield return () => global::System.Threading.Tasks.Task.FromResult(global::TUnit.Core.Helpers.DataSourceHelpers.ToObjectArray(item));
                                }
                            }
                            else
                            {
                                yield return () => global::System.Threading.Tasks.Task.FromResult(global::TUnit.Core.Helpers.DataSourceHelpers.ToObjectArray(result));
                            }
                        }
                        return Factory();
                    }
                },
            },
            ClassDataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
            PropertyDataSources = new global::TUnit.Core.PropertyDataSource[]
            {
                new global::TUnit.Core.PropertyDataSource
                {
                    PropertyName = "Fixture",
                    PropertyType = typeof(global::MyNamespace.ErrFixture<global::MyNamespace.MyType>),
                    DataSource = new global::TUnit.Core.ClassDataSourceAttribute<global::MyNamespace.ErrFixture<global::MyNamespace.MyType>>()
{Shared = global::TUnit.Core.SharedType.PerClass,},
                },
            },
            PropertyInjections = new global::TUnit.Core.PropertyInjectionData[]
            {
                new global::TUnit.Core.PropertyInjectionData
                {
                    PropertyName = "Fixture",
                    PropertyType = typeof(global::MyNamespace.ErrFixture<global::MyNamespace.MyType>),
                    #if NET8_0_OR_GREATER
                    Setter = (instance, value) => GetFixtureBackingField((global::MyNamespace.ErrTest)instance) = (global::MyNamespace.ErrFixture<global::MyNamespace.MyType>)value,
                    #else
                    Setter = (instance, value) => throw new global::System.NotSupportedException("Setting init-only properties requires .NET 8 or later"),
                    #endif
                    ValueFactory = () => throw new global::System.InvalidOperationException("ValueFactory should be provided by TestDataCombination"),
                    NestedPropertyInjections = global::System.Array.Empty<global::TUnit.Core.PropertyInjectionData>(),
                    NestedPropertyValueFactory = obj =>
                    {
                        return new global::System.Collections.Generic.Dictionary<string, object?>();
                    }
                },
            },
            InheritanceDepth = 0,
            FilePath = @"C:\\Users\\rasmus\\RiderProjects\\Hoeyer.Opcua\\Hoeyer.OpcUa.EndToEndTest\\ClientTests\\ErrTest.cs",
            LineNumber = 33,
            MethodMetadata = new global::TUnit.Core.MethodMetadata
            {
                Type = typeof(global::MyNamespace.ErrTest),
                TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::MyNamespace.ErrTest)),
                Name = "MyFailingTestIsHere",
                GenericTypeCount = 0,
                ReturnType = typeof(global::System.Threading.Tasks.Task),
                ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)),
                Parameters = new global::TUnit.Core.ParameterMetadata[]
                {
                    new global::TUnit.Core.ParameterMetadata(typeof(global::MyNamespace.ErrContext))
                    {
                        Name = "context",
                        TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::MyNamespace.ErrContext)),
                        IsNullable = false,
                        ReflectionInfo = typeof(global::MyNamespace.ErrTest).GetMethod("MyFailingTestIsHere", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(global::MyNamespace.ErrContext) }, null)!.GetParameters()[0]
                    }
                },
                Class = global::TUnit.Core.ClassMetadata.GetOrAdd("Hoeyer.OpcUa.EndToEndTest:global::MyNamespace.ErrTest", static () => 
                {
                    var classMetadata = new global::TUnit.Core.ClassMetadata
                    {
                        Type = typeof(global::MyNamespace.ErrTest),
                        TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::MyNamespace.ErrTest)),
                        Name = "ErrTest",
                        Namespace = "MyNamespace",
                        Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("Hoeyer.OpcUa.EndToEndTest", static () => new global::TUnit.Core.AssemblyMetadata { Name = "Hoeyer.OpcUa.EndToEndTest" }),
                        Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
                        Properties = new global::TUnit.Core.PropertyMetadata[]
                        {
                            new global::TUnit.Core.PropertyMetadata
                            {
                                ReflectionInfo = typeof(global::MyNamespace.ErrTest).GetProperty("Fixture"),
                                Type = typeof(global::MyNamespace.ErrFixture<global::MyNamespace.MyType>),
                                Name = "Fixture",
                                IsStatic = false,
                                IsNullable = false,
                                Getter = o => ((global::MyNamespace.ErrTest)o).Fixture,
                                ClassMetadata = null!,
                                ContainingTypeMetadata = null!
                            },
                            new global::TUnit.Core.PropertyMetadata
                            {
                                ReflectionInfo = typeof(global::MyNamespace.ErrTest).GetProperty("TestExecutions"),
                                Type = typeof(global::System.Collections.Generic.IEnumerable<global::System.Func<global::MyNamespace.ErrContext>>),
                                Name = "TestExecutions",
                                IsStatic = false,
                                IsNullable = false,
                                Getter = o => ((global::MyNamespace.ErrTest)o).TestExecutions,
                                ClassMetadata = null!,
                                ContainingTypeMetadata = null!
                            }
                        },
                        Parent = null
                    };
                    foreach (var prop in classMetadata.Properties)
                    {
                        prop.ClassMetadata = classMetadata;
                        prop.ContainingTypeMetadata = classMetadata;
                    }
                    return classMetadata;
                })
            },
            InstanceFactory = (typeArgs, args) => new global::MyNamespace.ErrTest()
            {
                Fixture = default!,
            },
            InvokeTypedTest = static (instance, args, cancellationToken) =>
            {
                try
                {
                    switch (args.Length)
                    {
                        case 1:
                            return new global::System.Threading.Tasks.ValueTask(instance.MyFailingTestIsHere(TUnit.Core.Helpers.CastHelper.Cast<global::MyNamespace.ErrContext>(args[0])));
                        default:
                            throw new global::System.ArgumentException($"Expected exactly 1 argument, but got {args.Length}");
                    }
                }
                catch (global::System.Exception ex)
                {
                    return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
                }
            },
        };
        metadata.UseRuntimeDataGeneration(testSessionId);
        yield return metadata;
        yield break;
    }
}
internal static class MyNamespace_ErrTest_MyFailingTestIsHere__ErrContext_ModuleInitializer
{
    [global::System.Runtime.CompilerServices.ModuleInitializer]
    public static void Initialize()
    {
        global::TUnit.Core.SourceRegistrar.Register(typeof(global::MyNamespace.ErrTest), new MyNamespace_ErrTest_MyFailingTestIsHere__ErrContext_TestSource());
    }
}

What would solve my problem

Either an analyser error from TUnit informing about an easy fix or for it to work automagically

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions