-
-
Notifications
You must be signed in to change notification settings - Fork 109
feat: prevent side effects from non-IAsyncInitializer property getters during discovery #4054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…s during discovery
Code Review for PR 4054SummaryThis PR fixes issue 4049 by preventing side effects from property getters during test discovery. The fix adds a type check before accessing properties to only evaluate those that could contain IAsyncInitializer instances. Strengths
Observations & Considerations1. Dual-Mode Implementation - This change only affects reflection mode (runtime discovery in TUnit.Engine). The source generator path handles test discovery at compile-time and wouldn't invoke property getters at runtime. Dual-mode implementation not required for this fix. 2. Type Safety Trade-off - The fix requires users to properly type their properties as IAsyncInitializer for automatic discovery. Properties typed as object won't be discovered. Consider adding an analyzer rule to detect this pattern and suggest proper typing. 3. Performance Impact - The type check is performed on the type, not the instance, with minimal overhead. Actually improves performance by skipping unnecessary property accesses. Potential Issues1. Breaking Change - Users who relied on implicit discovery of loosely-typed properties will need to update their code. Document this in release notes as a breaking change and consider adding a diagnostic analyzer. 2. Test Isolation - The test uses a static counter which could be problematic if tests run in parallel. Consider resetting the counter in a hook or making it instance-based. RecommendationsHigh Priority: Merge as-is - The core fix is sound and solves the immediate problem. Medium Priority (Follow-up): Document breaking change in release notes, create analyzer rule for loosely-typed properties, add test case for generic constraints. Checklist Review (per CLAUDE.md)
Final VerdictApprove with minor follow-up suggestions This is a well-crafted fix that solves the reported issue elegantly, improves performance, includes comprehensive tests, has minimal code footprint, and follows TUnit conventions. The type safety trade-off is reasonable. Great work! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes issue #4049 by preventing TUnit from calling property getters during discovery unless the property is typed as IAsyncInitializer. Previously, the reflection-based discovery would access all properties to check if their runtime value implements IAsyncInitializer, causing unintended side effects in properties like WebApplicationFactory.Server that perform initialization on access.
Key Changes:
- Added type check before property access in
TraverseInitializerPropertiesto skip properties not typed asIAsyncInitializer - Added comprehensive regression tests covering both the fix (preventing side effects) and ensuring nested
IAsyncInitializerproperties are still discovered
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| TUnit.Core/Discovery/ObjectGraphDiscoverer.cs | Added IsAssignableFrom check at line 451 to filter properties by declared type before calling getters, preventing side effects from unrelated properties |
| TUnit.TestProject/Bugs/4049/PropertyGetterSideEffectTests.cs | Added regression tests with two test classes: one verifying non-IAsyncInitializer properties aren't accessed, and one ensuring properly-typed IAsyncInitializer properties are still discovered |
| [EngineTest(ExpectedResult.Pass)] | ||
| public class PropertyGetterSideEffectTests |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test classes use static counters but lack the NotInParallel attribute. If these tests run concurrently, the static fields _sideEffectCount, _parentInitCount, and _childInitCount could experience race conditions, leading to flaky test failures. Add [NotInParallel] attribute to both test classes to ensure sequential execution.
| /// Test to verify that nested IAsyncInitializer properties ARE still discovered | ||
| /// when they are properly typed. | ||
| /// </summary> | ||
| [EngineTest(ExpectedResult.Pass)] |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test class uses static counters but lacks the NotInParallel attribute. If these tests run concurrently with other tests, the static fields _parentInitCount and _childInitCount could experience race conditions, leading to flaky test failures. Add [NotInParallel] attribute to ensure sequential execution.
| [EngineTest(ExpectedResult.Pass)] | |
| [EngineTest(ExpectedResult.Pass)] | |
| [NotInParallel] |
Fixes #4049