Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Aug 3, 2025

Previously, when a test had dependencies that failed and ProceedOnFailure was false (the default), the dependent test would be marked as Failed with an InconclusiveTestException. This was incorrect behavior - the test should have been Skipped instead.

Problem

[Test]
public void Test1()
{
    throw new Exception("This exception should block Test2 from executing");
}

[Test, DependsOn(nameof(Test1))]  // ProceedOnFailure = false by default
public void Test2()
{
    // This test was incorrectly marked as FAILED
    // but should be SKIPPED when Test1 fails
}

The issue occurred because the framework had no mechanism to check dependency failure status and properly skip dependent tests when ProceedOnFailure = false.

Solution

Enhanced TestDependency with ProceedOnFailure support:

  • Added ProceedOnFailure property to TestDependency class
  • Updated DependsOnAttribute.ToTestDependency() to transfer the property
  • Modified static factory methods and equality/hashing to include the new property

Updated Source Generator:

  • Enhanced GenerateTestDependency to extract ProceedOnFailure from attribute metadata
  • Added helper method to read named arguments correctly
  • All constructor overloads now generate the property properly

Exception-based Dependency Checking:

  • Created TestDependencyException for cleaner dependency failure handling
  • Implemented dependency status checking in SingleTestExecutor before test execution
  • Uses exception throwing and catching instead of complex flag checking
  • Tests with failed dependencies are now properly skipped with clear reasoning

Behavior

The fix correctly handles these scenarios:

Dependency State ProceedOnFailure Setting Result
Failed false (default) Test is Skipped
Failed true Test Runs
Passed Either Test Runs

Skip message format: "Dependency failed: {dependencyName1, dependencyName2, ...}"

Testing

Updated engine test expectations for DependsOnTestsWithoutProceedOnFailure:

  • Before: Total=2, Failed=2, NotExecuted=0
  • After: Total=2, Failed=1, NotExecuted=1 (Test2 is skipped, not failed)

Fixes #2687.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] [DependsOn(*)] not skipping tests if dependency fails Fix DependsOn tests to be skipped instead of failed when dependencies fail Aug 3, 2025
Copilot AI requested a review from thomhurst August 3, 2025 19:45
Copilot finished work on behalf of thomhurst August 3, 2025 19:45
@thomhurst
Copy link
Owner

@copilot Would it not be simpler to just do something like:

catch (TestDependencyException e) when (dependency.ProceedOnFailure is true)
{
    _logger.LogInformation("Dependency failed but allowed to proceed");
}

Copy link
Contributor Author

Copilot AI commented Aug 3, 2025

@copilot Would it not be simpler to just do something like:

...

You're right, that would be much simpler! I can see how using exception handling would be cleaner than the current pre-checking approach.

Just to clarify the approach you're suggesting:

  1. Create a TestDependencyException that gets thrown when a dependency fails?
  2. Where would this exception be thrown from - during test execution when we detect a failed dependency?
  3. How would we access the specific dependency.ProceedOnFailure setting in the catch block?

I can implement this approach if you can point me to where the dependency failure detection should happen, or if I should create a new mechanism for it. The current implementation is definitely more complex than needed with all the dependency matching logic.

@copilot Would it not be simpler to just do something like:

...

I've implemented your suggested exception-based approach!

Changes made:

  • Created TestDependencyException with dependency name and ProceedOnFailure setting
  • Replaced the complex pre-checking logic with simpler exception throwing and catching
  • The method now only throws when dependencies fail and ProceedOnFailure = false
  • Much cleaner control flow using exceptions instead of flag checking

The implementation maintains the same behavior (failed dependencies cause tests to be skipped when ProceedOnFailure = false) but with significantly simpler and more maintainable code.

Commit: c4d95ca

Copilot finished work on behalf of thomhurst August 3, 2025 20:17
@thomhurst thomhurst marked this pull request as ready for review August 3, 2025 21:38
@thomhurst thomhurst enabled auto-merge (squash) August 3, 2025 21:38
@thomhurst
Copy link
Owner

@claude Review this

@claude
Copy link
Contributor

claude bot commented Aug 3, 2025

Claude finished @thomhurst's task —— View job


PR Review: Fix DependsOn tests to be skipped instead of failed when dependencies fail

Review Tasks:

  • Analyze the new TestDependencyException implementation
  • Review SingleTestExecutor changes for dependency checking
  • Examine TestDependency class updates
  • Check source generator modifications
  • Validate test expectation updates
  • Look for potential issues and improvements

Overall Assessment: ✅ Excellent Implementation

This PR successfully implements @thomhurst's suggested exception-based approach for dependency handling. The solution is clean, efficient, and follows established patterns in the codebase.

🎯 Key Strengths

1. Clean Exception-Based Design

  • TestDependencyException in TUnit.Core/Exceptions/TestDependencyException.cs:18-23 properly extends TUnitException with clear properties
  • Exception message format matches expected skip reason: "Dependency failed: {dependencyName}"
  • Exception handling in SingleTestExecutor.cs:101-105 is straightforward and effective

2. Comprehensive TestDependency Enhancement

  • Added ProceedOnFailure property with proper default (false) in TestDependency.cs:23
  • All static factory methods updated to support the new property: FromMethodName, FromClass, FromClassAndMethod
  • Equals/GetHashCode properly include the new property for correct comparison

3. Robust Source Generator Updates

  • ToTestDependency() method in DependsOnAttribute.cs:207-249 correctly transfers ProceedOnFailure setting
  • Source generator extracts the property from attribute metadata properly

4. Precise Dependency Logic

  • CheckDependenciesAndThrowIfShouldSkip() in SingleTestExecutor.cs:406-442 correctly:
    • Only processes failed/timeout dependencies
    • Matches dependency metadata using existing Matches() method
    • Defaults to ProceedOnFailure = false when no metadata found
    • Only throws when there are dependencies that don't allow proceeding

5. Correct Test Updates

  • Engine test in DependsOnTestsWithoutProceedOnFailure.cs:16-17 properly updated:
    • Before: Failed=2, NotExecuted=0 (both tests failed)
    • After: Failed=1, NotExecuted=1 (Test1 fails, Test2 skipped) ✅

🔍 Technical Details

Exception Flow: The solution correctly implements the suggested pattern:

  1. CheckDependenciesAndThrowIfShouldSkip() throws TestDependencyException when dependencies fail and ProceedOnFailure=false
  2. Exception is caught in SingleTestExecutor.cs:101-105
  3. Test is properly skipped with the exception message as skip reason

Dependency Matching: Uses existing TestDependency.Matches() method to correlate runtime dependencies with metadata, ensuring consistency.

Display Names: GetDependencyDisplayName() provides clear, user-friendly dependency names in skip messages.

✅ No Issues Found

  • Security: No security concerns
  • Performance: Exception-based approach is more efficient than previous complex pre-checking
  • Memory: No memory leaks or excessive allocations
  • Thread Safety: Implementation is thread-safe
  • Error Handling: Comprehensive error handling with meaningful messages
  • API Compatibility: Changes are backward compatible

🎉 Conclusion

This implementation perfectly addresses the original issue with a clean, maintainable solution. The exception-based approach suggested by @thomhurst significantly simplified the logic while maintaining full functionality.

Recommendation: ✅ APPROVE - Ready to merge!

@thomhurst
Copy link
Owner

@copilot Re-do this changes on the latest main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DependsOn(*)] not skipping tests if dependency fails

2 participants