Fail assertion when path-based Excluding/Including rules are applied on value-semantic types#3167
Draft
Fail assertion when path-based Excluding/Including rules are applied on value-semantic types#3167
Conversation
…emantic types Co-authored-by: dennisdoomen <[email protected]>
Co-authored-by: dennisdoomen <[email protected]>
Copilot
AI
changed the title
[WIP] Fix behavior of member exclusions in FluentAssertions
Fail assertion when path-based Excluding/Including rules are applied on value-semantic types
Mar 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a type overrides
Equals, FluentAssertions compares it by value and silently ignores any path-basedExcluding()/Including()rules — making the assertion pass or fail for the wrong reason.Changes
SelectMemberByPathSelectionRule— Added abstractMemberPathproperty to expose the targeted member path from path-based selection rulesExcludeMemberByPathSelectionRule/IncludeMemberByPathSelectionRule— ImplementMemberPathValueTypeEquivalencyStep— WhenEqualityStrategy.Equalsis in effect (auto-detected, not explicitly forced viaComparingByValue<T>()), checks whether any path-based selection rules apply to the current node. If so, fails with a descriptive errorBehavior
Not affected:
ForceEqualsvia explicitComparingByValue<T>()— user opted in deliberately, no errorExcluding(member => ...)— cannot be attributed to a specific type/pathOriginal prompt
This section details on the original issue you should resolve
<issue_title>Excluding and Including options should fail when applied on types with value semantics</issue_title>
<issue_description>### Description
I am updating the FluentAssertions package on a project from a very old version that would always do member-wise comparisons for
Should().BeEquivalentTo(), even if the objects implementedIEquatable<T>. In general, I am ok with the new behavior, but here is a consequence I think is unexpected and undesirable: Explicit member exclusions are being completely ignored.IMO, FluentAssertions should consider the fact that the implementation of
IEquatable<T>.Equalsis opaque and hence cannot be assumed to work in any particular way, so it should automatically fall back to perform member-wise comparisons when the assertion is configured with any type of explicit member exclusion.In practice with the current behavior (which repros on 6.12 and 7.0.0 preview), I am forced to add
ComparingByMembers<T>()specifying an explicitT(because it cannot be inferred by the compiler, which adds to the friction) forBeEquivalentTo()call that usesExcluding().Reproduction Steps
Expected behavior
Explicit exclusions should never be ignored, and this assertion should pass:
Actual behavior
Exclusions are ignored so this assertion fails:
Regression?
This is a regression but only from very old FluentAssertions versions like 3.5.0.
Known Workarounds
ComparingByMembers<T>()wheneverExcluding()is also used. This is particularly painful becauseThas to be specified explicitly:Configuration
Repros on FA 6.12 and 7.0.0 preview.
Used to work fine with FA 3.5.0.
Tested on both .NET Framework 4.x and .NET 8.
Other information
Apart from this, the way FluentAssertions now leverages
IEquatable<T>.Equalsforced us to debug a bunch of incorrect equality evaluation code in our domain classes. While this is ultimately a good thing and I am thankful to FA team for this 😄, it feels like a different concern from what the tests were initially designed to cover. I know that the behavior can be customized, so this is just a comment on the choice of default behavior.Note: I am a newbie using FluentAssertions so I will be the first to admit that my mental model might be lacking. I apologize if I am being naive or if I missed any discussion in which it was decided that this was by design (I did a search on the repo, but I did not find anything that seemed relevant).
Are you willing to help with a pull-request?
Maybe 😄
I am not sure how long it can take me to understand the FluentAssertions codebase well enough.</issue_description>
Comments on the Issue (you are @copilot in this section)
@dennisdoomen Looking at the example a bit closer, I notice that you also override `Equals`. _That_ is the reason why FA assumes you want it to treat the type as having value semantics. It doesn'...🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.