-
-
Notifications
You must be signed in to change notification settings - Fork 106
fix: prevent IAsyncInitializer from running during test discovery when using InstanceMethodDataSource #4002
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
…n using InstanceMethodDataSource
Pull Request ReviewSummaryThis PR fixes issue #3992 by preventing IAsyncInitializer objects from being initialized during test discovery when using InstanceMethodDataSource. Strengths1. Correct Fix StrategyThe fix is architecturally sound. By checking 'is IAsyncDiscoveryInitializer' before calling ObjectInitializer.InitializeAsync(), it provides users explicit control over when initialization happens. This prevents expensive operations like Docker containers from running during IDE test discovery. 2. Comprehensive Test CoverageThe test file InstanceMethodDataSourceWithAsyncInitializerTests.cs is exemplary with well-documented explanations, realistic scenario simulating Docker containers, concrete assertions, and verification of shared fixture behavior. 3. Consistent ApplicationThe fix is applied to all 5 locations in DataSourceHelpers.cs ensuring no code path is missed. 4. Clear CommentsEach change includes helpful comments explaining the rationale. Code Quality
AnalysisEdge CasesSince IAsyncDiscoveryInitializer extends IAsyncInitializer, the check handles mixed implementations correctly. ObjectInitializer tracks initialized objects preventing double-initialization. PerformanceNegligible performance impact from type checks, but massive gain in preventing unwanted initialization during discovery. ConsistencyI verified that TUnit.Engine/Building/TestBuilder.cs has similar logic. The fix aligns with existing patterns. TestingAll required tests are covered. The test quality is production-grade and serves as excellent documentation. Security and Performance
Alignment with TUnit Philosophy
Final VerdictThis PR is ready to merge. Demonstrates clear understanding, elegant minimal solution, thorough testing, and alignment with TUnit conventions. Recommendation: APPROVE AND MERGE Great work on this fix! The test documentation is particularly well-crafted. |
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.
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| private readonly List<string> _testCases = []; | ||
|
|
||
| /// <summary> | ||
| /// Unique identifier for this instance to verify sharing behavior. | ||
| /// </summary> | ||
| public Guid InstanceId { get; } = Guid.NewGuid(); | ||
|
|
||
| public bool IsInitialized { get; private set; } | ||
| public IReadOnlyList<string> TestCases => _testCases; | ||
|
|
||
| public Task InitializeAsync() | ||
| { | ||
| Interlocked.Increment(ref _initializationCount); | ||
| Console.WriteLine($"[SimulatedContainerFixture] InitializeAsync called on instance {InstanceId} (count: {_initializationCount})"); | ||
|
|
||
| // Simulate container startup that populates test data | ||
| _testCases.AddRange(["TestCase1", "TestCase2", "TestCase3"]); |
Copilot
AI
Dec 6, 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.
Critical Test Design Flaw: _testCases starts as an empty list and is only populated during InitializeAsync(). After the fix, InitializeAsync() won't be called during discovery, so Fixture.TestCases will be empty when InstanceMethodDataSource evaluates TestExecutions at line 64. This means zero test cases will be generated, and the test will never execute.
This contradicts the test's purpose - the After(Class) hook at line 88 expects to verify that exactly 3 tests ran and initialization happened once. But with empty test cases, no tests will run, and the verification will never execute.
Solution: Initialize _testCases with default data before InitializeAsync(), so test discovery can proceed:
private readonly List<string> _testCases = ["TestCase1", "TestCase2", "TestCase3"];
public Task InitializeAsync()
{
Interlocked.Increment(ref _initializationCount);
// Do something else to prove initialization ran (e.g., set a flag, modify data, etc.)
IsInitialized = true;
return Task.CompletedTask;
}Note: If users genuinely need data populated during discovery (like the original issue #3992), they should use IAsyncDiscoveryInitializer instead of IAsyncInitializer (see Bug 3997 test for correct pattern).
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.
@copilot reanalyse
| [InstanceMethodDataSource(nameof(TestExecutions))] | ||
| public async Task Test_WithInstanceMethodDataSource_DoesNotInitializeDuringDiscovery(string testCase) | ||
| { | ||
| Interlocked.Increment(ref _testExecutionCount); |
Copilot
AI
Dec 6, 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.
Write to static field from instance method, property, or constructor.
…using predefined test case identifiers
Pull Request Review - Fix for Issue #3992Thank you for this fix! This PR addresses a critical bug where ✅ Strengths1. Correct Problem IdentificationThe fix properly distinguishes between:
This is the right design pattern and follows the framework's intent from PR #4000. 2. Comprehensive CoverageThe changes cover all the relevant code paths in
3. Excellent Test CoverageThe test file
4. Clear Code CommentsEach conditional check includes a comment explaining why only 🔍 Potential Issues & Improvements1. Dual-Mode Implementation
|
| Aspect | Rating | Notes |
|---|---|---|
| Correctness | ✅ Excellent | Fixes the reported issue |
| Code Style | ✅ Good | Follows C# conventions, clear comments |
| Test Coverage | ✅ Excellent | Comprehensive test with good documentation |
| Performance | ✅ Good | No performance regression |
| Security | ✅ N/A | No security concerns |
| Dual-Mode | Reflection mode ✅, Source-gen mode ❓ |
📋 Pre-Commit Checklist (from CLAUDE.md)
Based on the repository's guidelines:
- ✅ Code follows modern C# style
- ✅ Tests added for the fix
⚠️ Dual-mode implementation: Needs verification for source-generated mode- ❓ Snapshot tests: If source generator output changed, did you run
TUnit.Core.SourceGenerator.Testsand accept snapshots? - ❓ Public API: Did this change any public APIs? If so, run
TUnit.PublicAPItests
🎓 Recommendations
Must Do:
- Verify source-generated mode handles this correctly (or document why it's not needed)
- Run snapshot tests if applicable
Should Do:
- Consider extracting the repeated conditional logic into a helper method
- Add edge case test for objects implementing both
IAsyncInitializerandIAsyncDiscoveryInitializer
Nice to Have:
- Add test coverage for different
SharedTypescenarios - Add integration test that verifies the fix with actual
--list-testscommand
🏁 Conclusion
This is a high-quality fix that correctly addresses issue #3992. The code is clean, well-documented, and includes excellent test coverage.
Main Action Items:
- Verify dual-mode implementation (source-generated tests)
- Consider the refactoring suggestions to reduce code duplication
- Ensure snapshot tests pass if applicable
Great work on the detailed test documentation - it makes the intent and behavior crystal clear! 🎉
|
@thomhurst I've opened a new pull request, #4003, to work on those changes. Once the pull request is ready, I'll request review from you. |
Fixes #3992