Fix generic DependsOn<T> attribute not working#4246
Conversation
…e argument Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where the generic DependsOn<T> attribute was ignoring the type parameter T, treating cross-class dependencies as same-class dependencies. The root cause was that the source generator only examined constructor arguments, missing the generic type argument which is stored separately on the attribute class itself.
Key Changes
- The source generator now extracts the type argument from
AttributeClass.TypeArguments[0]when the attribute is generic - Adds handling for all three generic constructor overloads:
DependsOn<T>(),DependsOn<T>(methodName), andDependsOn<T>(methodName, parameterTypes) - Integration tests verify both generic dependency patterns work correctly
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs | Modified GenerateTestDependency() to detect generic DependsOnAttribute<T> and extract the type argument, then generate appropriate TestDependency objects with the correct ClassType for all three constructor overloads |
| TUnit.TestProject/GenericDependsOnTests.cs | Added integration tests for both DependsOn<T>(methodName) and DependsOn<T>() patterns using timing assertions to verify dependency ordering is respected |
| [Test] | ||
| public async Task Test1() | ||
| { | ||
| Test1Start = TestContext.Current!.Execution.TestStart!.Value.DateTime; |
There was a problem hiding this comment.
Write to static field from instance method, property, or constructor.
| [Test, DependsOn<GenericDependsOnTestsClassA>(nameof(GenericDependsOnTestsClassA.Test1))] | ||
| public async Task Test2() | ||
| { | ||
| _test2Start = TestContext.Current!.Execution.TestStart!.Value.DateTime; |
There was a problem hiding this comment.
Write to static field from instance method, property, or constructor.
| [Test] | ||
| public async Task Test1() | ||
| { | ||
| Test1Start = TestContext.Current!.Execution.TestStart!.Value.DateTime; |
There was a problem hiding this comment.
Write to static field from instance method, property, or constructor.
| [Test, DependsOn<GenericDependsOnTestsClassB>] | ||
| public async Task Test2() | ||
| { | ||
| _test2Start = TestContext.Current!.Execution.TestStart!.Value.DateTime; |
There was a problem hiding this comment.
Write to static field from instance method, property, or constructor.
| [Test, DependsOn<GenericDependsOnTestsClassB>] | |
| public async Task Test2() | |
| { | |
| _test2Start = TestContext.Current!.Execution.TestStart!.Value.DateTime; | |
| private static void SetTest2Start(DateTime value) | |
| { | |
| _test2Start = value; | |
| } | |
| [Test, DependsOn<GenericDependsOnTestsClassB>] | |
| public async Task Test2() | |
| { | |
| SetTest2Start(TestContext.Current!.Execution.TestStart!.Value.DateTime); |
Generic
DependsOn<T>was ignoring the type parameterT, treating[DependsOn<ClassA>("MethodName")]as[DependsOn("MethodName")](same-class dependency).Root cause: The source generator only examined constructor arguments. For generic attributes,
Tis a type argument on the attribute class, not a constructor argument.Fix: Extract type from
attributeData.AttributeClass.TypeArguments[0]when attribute is generic.Changes
TestMetadataGenerator.GenerateTestDependency()- Check for genericDependsOnAttribute<T>and extract type argument for all three constructor overloads:DependsOn<T>()- all tests in class TDependsOn<T>(methodName)- specific test in class TDependsOn<T>(methodName, parameterTypes)- specific overload in class TGenericDependsOnTests.cs- Integration tests for bothDependsOn<T>()andDependsOn<T>(methodName)patternsExample
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.