Repository navigation
Description
What happens
A generic test method compiles to broken code when its class takes constructor arguments from a class-level data source, if the method's type argument is fixed with [GenerateGenericTest]. The source generator emits __CreateInstance_0 as new TestClass() and ignores args, so the build fails with CS7036.
public sealed class ServiceDependencyInjectionAttribute : DependencyInjectionDataSourceAttribute<MyScope> { /* ... */ }
[ServiceDependencyInjection]
public class GenericMethodWithClassArgsTests(MyService service)
{
public static IEnumerable<object> Rows() => [1];
[Test]
[GenerateGenericTest(typeof(int))]
[MethodDataSource(nameof(Rows))]
public async Task Run<T>(T value) => await Assert.That(service).IsNotNull();
}TUnit_..._GenericMethodWithClassArgsTests_Run__T.g.cs(31,20): error CS7036: There is no argument given that corresponds to the required parameter 'service' of 'GenericMethodWithClassArgsTests.GenericMethodWithClassArgsTests(MyService)'
Generated code (net10.0, TUnit.Core.SourceGenerator, TestMetadataGenerator):
private static global::...GenericMethodWithClassArgsTests __CreateInstance_0(global::System.Type[] typeArgs, object?[] args)
{
return new global::...GenericMethodWithClassArgsTests();
}Where
Found while adding a regression test to #7328, at tests/TUnit.TestProject/Bugs/_7205 (the test was dropped). The instance factory for a generic method's concrete instantiation is written in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs.
Expected
__CreateInstance_0 passes args to the constructor, as the non-generic __CreateInstance does (for example TUnit_TestProject_Bugs__7205_FailedRowScopeTests__TestSource.g.cs). Reflection mode should be checked too: it builds this class without a compile error, but whether it passes the arguments is unverified.
Acceptance criteria
- A generic method fixed by
[GenerateGenericTest]on a non-generic class with constructor data compiles and receives every constructor argument. - A targeted fixture passes in source-generated and reflection modes, asserting constructor and method values.
- Add and review a generator snapshot that checks the concrete instance factory uses
args.
Current-main evidence: TryAddInstantiation sets HasParameterizedConstructor only inside if (testMethod.IsGenericType); EmitConcreteInstanceCreation falls back to parameterless new for the non-generic class/generic-method combination.
Activity
thomhurst commentedon Nov 3, 2026
Audited against
origin/maind87516c:TryAddInstantiationsetsHasParameterizedConstructoronly for a generic class (lines 1043–1054). A generic method on a non-generic class therefore reaches the parameterlessnewfallback inEmitConcreteInstanceCreation(line 1937). Added concrete acceptance criteria; reflection behavior still needs verification.thomhurst commentedon Nov 5, 2026
Already implemented by #7344, merged as a412ddd, and still present on current main (1a40c80).
EmitConcreteInstanceCreationnow routes a non-generic class of a generic method throughInstanceFactoryGenerator.GenerateInstanceFactoryBody(testMethod.TypeSymbol), passing constructor arguments instead of emitting a parameterlessnew.__CreateInstance_0constructingConstructorArgumentGenerateGenericTestArityTestswithCastHelper.Cast<int>(args[0]).InvokableTestBase.Verified the implementation and committed coverage against current main; no new code changes were needed. Did not rerun the engine suite locally, as required by the repository instructions. Closing as already fixed.