Fail with a descriptive error when path-based rules are used on value-semantic types#3187
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test Results 37 files ± 0 37 suites ±0 5m 57s ⏱️ -11s Results for commit 580ef79. ± Comparison against base commit efe9635. This pull request removes 10 and adds 18 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
Coverage Report for CI Build 24278160846Coverage decreased (-0.04%) to 97.159%Details
Uncovered Changes
Coverage Regressions9 previously-covered lines in 2 files lost coverage.
Coverage Stats
💛 - Coveralls |
There was a problem hiding this comment.
Pull request overview
This PR addresses surprising behavior in BeEquivalentTo() where path-based selection rules (Excluding() / Including()) can be silently ignored when a type is auto-compared by value semantics (EqualityStrategy.Equals). It introduces conflict detection to fail fast with a descriptive assertion message.
Changes:
- Detect and report conflicts between auto-detected value-semantics comparison and path-based selection rules during equivalency comparison.
- Add
SelectsMembersOf(INode)to path-based selection rules to encapsulate overlap detection logic. - Add/extend selection rule specs and document the behavior change in the release notes.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/_pages/releases.md | Adds a release note describing the new fail-fast behavior for conflicting path rules + value semantics. |
| Tests/FluentAssertions.Equivalency.Specs/SelectionRulesSpecs.Including.cs | Adds a test asserting Including() fails with a descriptive message on value-semantic types. |
| Tests/FluentAssertions.Equivalency.Specs/SelectionRulesSpecs.Excluding.cs | Adds tests asserting Excluding() fails (and explicit overrides do/don’t fail). |
| Src/FluentAssertions/Equivalency/Steps/ValueTypeEquivalencyStep.cs | Adds conflict detection and emits a dedicated assertion failure instead of silently ignoring path rules. |
| Src/FluentAssertions/Equivalency/Selection/SelectMemberByPathSelectionRule.cs | Implements overlap detection (SelectsMembersOf) and normalizes current node paths. |
| Src/FluentAssertions/Equivalency/Selection/IncludeMemberByPathSelectionRule.cs | Refactors to expose the rule MemberPath via the base class for conflict detection. |
| Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs | Refactors to expose the rule MemberPath via the base class for conflict detection. |
6aa97a2 to
32702e1
Compare
Detect conflicts between Including/Excluding selection rules and value-semantic comparison so Fluent Assertions reports a descriptive error instead of silently ignoring user intent. This now covers both auto-detected Equals-based value semantics and explicit ComparingByValue() configuration. Use a path-aware selection-rule abstraction so collection decorators can forward path conflict checks without special-case unwrapping, and add coverage for path, predicate, nested, collection, and explicit configuration scenarios. Fixes #2571 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
32702e1 to
580ef79
Compare
Summary
Closes #2571
When
Excluding()orIncluding()targets a member of a type that is compared by value, the rule is silently ignored becauseBeEquivalentTo()never traverses that type’s members. This is surprising and hard to debug.This PR detects that conflict at comparison time and fails with a descriptive assertion error.
Changes
This PR now reports a descriptive failure when a selection rule targets a type that is being compared by value, including both:
EqualityStrategy.Equals) when the type overridesEquals()EqualityStrategy.ForceEquals) when the user callsComparingByValue()Example message for auto-detected value semantics:
Example message for explicit
ComparingByValue():All user-specified selection rule types are detected:
Excluding(o => o.Prop)Including(o => o.Prop)Excluding(m => m.Name == "Etag")Including(m => m.Name == "Id")Excluding<TMemberType>()Design decisions
EqualsandComparingByValue())..For(x => x.Items).Exclude(i => i.Name)are matched against concrete item paths likeItems[0].Name.AllPropertiesSelectionRule,AllFieldsSelectionRule,ExcludeNonBrowsableMembersRule) are skipped since they are not user-specified selections.Tests
Added coverage for:
.For(...).Exclude(...)ComparingByValue()with path-based exclusionComparingByValue()with path-based inclusionComparingByValue()with predicate-based exclusionComparingByMembers()continuing to work normally