Skip to content

Conversation

@FabianNitsche
Copy link
Contributor

This fixes #1613.

When an instance to compare throws an exception during the invocation of the HandleMethod in the Handle method of GenericEnumerableEquivalencyStep, the TargetInvocationException is unwrapped (to give the innermost exception) and rethrown. This previously lead to a wrong stack trace that would indicate, that something is wrong in GenericEnumerableEquivalencyStep.

The first commit provides a test that shows this.

The second commit fixes it by using ExceptionDispatchInfo to capture the stack trace. Since the class ExceptionExtensions is internal, the signature change of the Unwrap method is not changing the public API.

If you do not like the change of the method signature, there is a hack to keep it.

I read that I should add this fix to the release notes. In this branch or another branch?

@dennisdoomen dennisdoomen requested a review from jnyrup June 22, 2021 12:44
Copy link
Member

@jnyrup jnyrup left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, here's a dump of how this PR changes the stack trace.

act.Should().Throw<NotImplementedException>()
    .Which.StackTrace.Should().Contain("ExceptionThrowingProperty");
System.NotImplementedException: The method or operation is not implemented.
   at FluentAssertions.Equivalency.Steps.GenericEnumerableEquivalencyStep.Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\Steps\GenericEnumerableEquivalencyStep.cs:line 52
   at FluentAssertions.Equivalency.EquivalencyValidator.RunStepsUntilEquivalencyIsProven(Comparands comparands, IEquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 65
   at FluentAssertions.Equivalency.EquivalencyValidator.RecursivelyAssertEquality(Comparands comparands, IEquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 37
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 19
   at FluentAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1 expectation, Func`2 config, String because, Object[] becauseArgs) in C:\dev\fluentassertions\Src\FluentAssertions\Collections\GenericCollectionAssertions.cs:line 333
   at FluentAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1 expectation, String because, Object[] becauseArgs) in C:\dev\fluentassertions\Src\FluentAssertions\Collections\GenericCollectionAssertions.cs:line 284
   at FluentAssertions.Specs.Equivalency.CollectionEquivalencySpecs.<>c__DisplayClass113_0.<When_an_exception_is_thrown_during_data_access_the_stack_trace_contains_the_original_site>b__0() in C:\dev\fluentassertions\Tests\FluentAssertions.Specs\Equivalency\CollectionEquivalencySpecs.cs:line 2813
   at FluentAssertions.Specs.Equivalency.CollectionEquivalencySpecs.When_an_exception_is_thrown_during_data_access_the_stack_trace_contains_the_original_site() in C:\dev\fluentassertions\Tests\FluentAssertions.Specs\Equivalency\CollectionEquivalencySpecs.cs:line 2817

Notice the second line not present in the trace above.

System.NotImplementedException: The method or operation is not implemented.
   at FluentAssertions.Specs.Equivalency.CollectionEquivalencySpecs.ExceptionThrowingClass.get_ExceptionThrowingProperty() in C:\dev\fluentassertions\Tests\FluentAssertions.Specs\Equivalency\CollectionEquivalencySpecs.cs:line 196
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at FluentAssertions.Equivalency.Steps.GenericEnumerableEquivalencyStep.Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\Steps\GenericEnumerableEquivalencyStep.cs:line 52
   at FluentAssertions.Equivalency.EquivalencyValidator.RunStepsUntilEquivalencyIsProven(Comparands comparands, IEquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 65
   at FluentAssertions.Equivalency.EquivalencyValidator.RecursivelyAssertEquality(Comparands comparands, IEquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 37
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context) in C:\dev\fluentassertions\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 19
   at FluentAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1 expectation, Func`2 config, String because, Object[] becauseArgs) in C:\dev\fluentassertions\Src\FluentAssertions\Collections\GenericCollectionAssertions.cs:line 333
   at FluentAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1 expectation, String because, Object[] becauseArgs) in C:\dev\fluentassertions\Src\FluentAssertions\Collections\GenericCollectionAssertions.cs:line 284
   at FluentAssertions.Specs.Equivalency.CollectionEquivalencySpecs.<>c__DisplayClass113_0.<When_an_exception_is_thrown_during_data_access_the_stack_trace_contains_the_original_site>b__0() in C:\dev\fluentassertions\Tests\FluentAssertions.Specs\Equivalency\CollectionEquivalencySpecs.cs:line 2813
   at FluentAssertions.Specs.Equivalency.CollectionEquivalencySpecs.When_an_exception_is_thrown_during_data_access_the_stack_trace_contains_the_original_site() in C:\dev\fluentassertions\Tests\FluentAssertions.Specs\Equivalency\CollectionEquivalencySpecs.cs:line 2817

@dennisdoomen Should this be targeted against the release-6.0 branch?

@dennisdoomen
Copy link
Member

Should this be targeted against the release-6.0 branch?

Yes, I think it's totally fine to get it in 6.0

@FabianNitsche
Copy link
Contributor Author

Do I have to do something?

@dennisdoomen
Copy link
Member

Yes, to rebase it on the support-6.0 branch and change the target of the PR. Then it'll get part of the next 6.0 beta.

…e is returned from the unwrapping of the TargetInvocationException in GenericEnumerableEquivalencyStep.
…nner exception of the TargetInvocationException, so that the error message contains the correct stack trace.
@FabianNitsche FabianNitsche changed the base branch from develop to release-6.0 June 27, 2021 21:00
@FabianNitsche
Copy link
Contributor Author

Done.

@dennisdoomen
Copy link
Member

Oh crap. We also forgot about the release notes. Would you mind adding a line to https://github.com/fluentassertions/fluentassertions/blob/release-6.0/docs/_pages/releases.md?

@FabianNitsche
Copy link
Contributor Author

Done.
BTW: This was actually my question in the first post... ;D

@dennisdoomen dennisdoomen merged commit 3f0f528 into fluentassertions:release-6.0 Jun 28, 2021
@dennisdoomen
Copy link
Member

Thanks for the contribution!

@FabianNitsche FabianNitsche deleted the improvedStackTrace branch June 28, 2021 12:44
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.

Improve Stack Trace for InnerException of TargetInvocationExceptions

3 participants