Skip to content

Add BeSupersetOf, BeProperSubsetOf and BeProperSupersetOf collection assertions - #3271

Merged
dennisdoomen merged 4 commits into
fluentassertions:mainfrom
dennisdoomen:feature/subset-superset-collections
Aug 9, 2026
Merged

dennisdoomen merged 4 commits into
fluentassertions:mainfrom
dennisdoomen:feature/subset-superset-collections

Conversation

@dennisdoomen

@dennisdoomen dennisdoomen commented Jul 18, 2026

Copy link
Copy Markdown
Member

Ports the work originally submitted in #2432 by @Meir017 ( Meir017:feature/api-subset-and-superset), which added BeSupersetOf, BeProperSubsetOf and BeProperSupersetOf collection assertions.

That PR could not be reopened because its base branch (develop) has since been deleted/renamed to main, so this PR recreates the same feature against current main.

What's included

  • BeSupersetOf / BeProperSubsetOf / BeProperSupersetOf assertions on GenericCollectionAssertions
  • ConvertOrCastToSet extension in EnumerableExtensions
  • Extracted AssertSubsetOf / AssertContainment helpers so BeSubsetOf / Contain share logic with the new assertions
  • Ported and adjusted spec tests from the original PR (adapted to the current AssertionChain API; fixed a couple of tests that relied on .And.-chaining the same assertions instance, which no longer replays a second failing assertion once the chain has already failed)
  • Updated docs/_pages/collections.md and docs/_pages/releases.md
  • Regenerated API approval baselines (AcceptApiChanges.ps1)

Closes #2432
Resolves #2363

All credit for the original design and implementation goes to @Meir017.

Empty-collection behavior

Following up on review feedback, here's how each assertion behaves when the expected collection is empty:

Assertion Expectation Subject Result
BeSubsetOf empty empty Passes
BeSubsetOf empty non-empty Fails (subject has extra items)
BeProperSubsetOf empty empty Fails (two empty collections are identical, so it's not a proper subset)
BeSupersetOf empty empty or non-empty Passes (there's nothing left to find in an empty expectation, so it's always satisfied)
BeProperSupersetOf empty empty Fails (identical collections aren't a proper superset)
BeProperSupersetOf empty non-empty Passes (any non-empty collection is a proper superset of an empty one)
any of the four null any Throws ArgumentNullException

BeSupersetOf and BeProperSupersetOf previously threw ArgumentException for an empty expectation instead of evaluating normally - that's now fixed so they behave consistently with BeSubsetOf/BeProperSubsetOf.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds new set-containment style collection assertions to FluentAssertions (BeSupersetOf, BeProperSubsetOf, BeProperSupersetOf) and factors shared subset/containment logic to reduce duplication, along with tests, docs, and updated API approval baselines.

Changes:

  • Added BeSupersetOf, BeProperSubsetOf, and BeProperSupersetOf to GenericCollectionAssertions.
  • Extracted shared helpers (AssertSubsetOf, AssertContainment) and introduced ConvertOrCastToSet for set-based checks.
  • Added/ported specs and updated collection docs + release notes + API approval baselines.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeSupersetOf.cs New specs covering BeSupersetOf success/failure and scope behavior.
Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeProperSupersetOf.cs New specs covering BeProperSupersetOf, including equivalence and duplicates scenarios.
Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeProperSubsetOf.cs New specs covering BeProperSubsetOf, including equivalence and null/empty scenarios.
Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt API baseline updated for new assertions.
Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt API baseline updated for new assertions.
Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt API baseline updated for new assertions.
Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt API baseline updated for new assertions.
Src/FluentAssertions/Common/EnumerableExtensions.cs Adds internal ConvertOrCastToSet helper for set-based operations.
Src/FluentAssertions/Collections/GenericCollectionAssertions.cs Implements the new assertions and refactors subset/containment logic into helpers.
docs/_pages/releases.md Adds an “Unreleased” note about the new collection assertions.
docs/_pages/collections.md Documents usage examples for the new assertions.
.gitignore Fixes .junie/memory/ ignore entry and adds .nuke/temp.
Suppressed comments (1)

Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeProperSupersetOf.cs:68

  • Test name says "proper subset" but this spec is for BeProperSupersetOf. Rename to "proper superset" to match the behavior under test.
        public void A_collection_with_duplicates_is_not_a_proper_subset_of_another_with_duplicates()

Comment thread Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Comment thread Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Comment thread docs/_pages/releases.md Outdated
…assertions

Ports the work originally submitted in PR fluentassertions#2432 by @Meir017, adapted to
the current AssertionChain-based assertion API. The original PR could
not be reopened because its base branch (develop) no longer exists.

- Add ConvertOrCastToSet extension to EnumerableExtensions
- Extract AssertSubsetOf and AssertContainment helpers in
  GenericCollectionAssertions to share logic with the new assertions
- Add BeSupersetOf, BeProperSubsetOf and BeProperSupersetOf methods
- Add spec tests ported from the original PR
- Update collections.md docs and releases.md

Co-authored-by: Meir017 <Meir017@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 1, 2026 08:24
@dennisdoomen
dennisdoomen force-pushed the feature/subset-superset-collections branch from f6d8d37 to eb4bd82 Compare August 1, 2026 08:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeProperSubsetOf.cs:54

  • This test name says the collection "has extra items", but the Arrange section is asserting that duplicates do not make a collection a proper subset when the distinct items are equivalent to the superset. Rename the test to describe equivalence rather than extra elements.
        public void A_collection_with_all_items_and_duplicates_of_a_superset_but_has_extra_items_is_not_a_proper_subset()

Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeProperSubsetOf.cs:38

  • This test name says the collection "has extra items", but the Arrange section uses two collections with the same distinct items (they're equivalent as sets). Rename the test to reflect that the failure is due to equivalence (not extra elements).

This issue also appears on line 54 of the same file.

        public void A_collection_with_all_items_of_a_superset_but_has_extra_items_is_not_a_proper_subset()

@jnyrup jnyrup left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We have an inconsistency in whether we allow the expectation to be empty.
E.g. when both subject and expectation are empty we get the following outcomes.

  • BeSubsetOf: passes
  • BeProperSubsetOf: Expected subset to be a proper subset of {empty}, but items {empty} are equivalent to the superset {empty}
  • BeSupersetOf: ArgumentException : Cannot verify containment against an empty collection (Parameter 'expected')
  • BeProperSupersetOf: ArgumentException : Cannot verify containment against an empty collection (Parameter 'expected')

I think the superset APIs should allow the expectation to be empty.
See e.g. the discussion in #2143

Comment thread Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeSupersetOf.cs Outdated
Addresses jnyrup's review feedback on fluentassertions#3271: BeSupersetOf and
BeProperSupersetOf previously threw ArgumentException when the
expected collection was empty, which was inconsistent with
BeSubsetOf/BeProperSubsetOf (an empty set is trivially a subset of
everything, so a superset check against an empty expectation should
never throw).

- Split the empty-argument guard out of AssertContainment behind an
  allowEmptyExpectation flag so Contain keeps its existing behavior
  while BeSupersetOf/BeProperSupersetOf now evaluate normally instead
  of throwing.
- BeSupersetOf(empty) now always passes.
- BeProperSupersetOf(empty) now passes when the subject is non-empty
  and fails (not throws) when the subject is also empty, since two
  empty collections are identical rather than a proper superset.
- Rewrote the XML doc comments for BeSubsetOf/BeSupersetOf/
  BeProperSubsetOf/BeProperSupersetOf in plain language, removing set
  notation and math jargon, and dropped the now-inaccurate
  ArgumentException doc entries for the superset methods.
- Wrapped single-element expectations in curly braces in the new
  superset failure messages, matching how multi-element collections
  are already rendered.
- Added missing spec coverage: null expectation, both empty, and
  equal-non-empty collections for BeSupersetOf, BeProperSubsetOf and
  BeProperSupersetOf.
- Updated docs/_pages/collections.md and releases.md.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@dennisdoomen

Copy link
Copy Markdown
Member Author

We have an inconsistency in whether we allow the expectation to be empty. E.g. when both subject and expectation are empty we get the following outcomes.

  • BeSubsetOf: passes
  • BeProperSubsetOf: Expected subset to be a proper subset of {empty}, but items {empty} are equivalent to the superset {empty}
  • BeSupersetOf: ArgumentException : Cannot verify containment against an empty collection (Parameter 'expected')
  • BeProperSupersetOf: ArgumentException : Cannot verify containment against an empty collection (Parameter 'expected')

I think the superset APIs should allow the expectation to be empty. See e.g. the discussion in #2143

Good points indeed. I think I have all scenarios covered now.

…ainment

Fixes a CI failure (StackTraceHiddenSpecs.Non_public_methods_in_annotable_classes_are_hidden)
surfaced after modifying these private helper methods.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread docs/_pages/releases.md Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This was referenced Sep 15, 2026
hannesbarbez pushed a commit to hannesbarbez/BarbezDotEu.Playwright that referenced this pull request Sep 15, 2026
Updated
[FluentAssertions](https://github.com/fluentassertions/fluentassertions)
from 8.10.0 to 8.11.0.

<details>
<summary>Release notes</summary>

_Sourced from [FluentAssertions's
releases](https://github.com/fluentassertions/fluentassertions/releases)._

## 8.11.0

<!-- Release notes generated using configuration in .github/release.yml
at main -->

## What's Changed
### New features
* Add `ThatSatisfy` for methods and properties by @​jnyrup in
fluentassertions/fluentassertions#3257
* Add BeSupersetOf, BeProperSubsetOf and BeProperSupersetOf collection
assertions by @​dennisdoomen in
fluentassertions/fluentassertions#3271
* Add BeJsonSerializable round-trip assertions by @​dennisdoomen in
fluentassertions/fluentassertions#3293
* Support asserting on delegates that return a value task by
@​dennisdoomen in
fluentassertions/fluentassertions#3301
### Improvements
* BeEmpty for IEnumerable<T> assertions now lists the first 10 items in…
by @​Xceed-DelvaJB in
fluentassertions/fluentassertions#3198
* Clean-up the stack trace further by @​jnyrup in
fluentassertions/fluentassertions#3290
* Add HaveLineCount()/NotHaveLineCount() and
ContainLine()/NotContainLine() to StringAssertions by @​dennisdoomen in
fluentassertions/fluentassertions#3297
* Add public ValueTypeAssertions<TSubject, TAssertions> base class by
@​dennisdoomen in
fluentassertions/fluentassertions#3308
### Fixes
* Fix JsonNodeAssertions.HaveProperty/NotHaveProperty for properties
with explicit null values by @​dennisdoomen with @​Copilot in
fluentassertions/fluentassertions#3282
* Fix exception on `HaveProperty` on `JsonArray` by @​jnyrup in
fluentassertions/fluentassertions#3295
* Fix potential NREs when using AssertionScope (#​3178) by
@​dennisdoomen in
fluentassertions/fluentassertions#3323
### Documentation
* Fix typos by @​jnyrup in
fluentassertions/fluentassertions#3310
* Fix typo in exceptions page by @​jpgomezv in
fluentassertions/fluentassertions#3317
### Others
* Simplify GetMinValue/GetMaxValue by @​jnyrup in
fluentassertions/fluentassertions#3210
* Revert "Bump coverlet.collector from 6.0.4 to 8.0.1" by @​jnyrup in
fluentassertions/fluentassertions#3218
* Update nugets by @​jnyrup in
fluentassertions/fluentassertions#3226
* Convert the build pipeline from Nuke to Fallout by @​dennisdoomen in
fluentassertions/fluentassertions#3234
* Expose ShouldThrowOnCyclicReference on IEquivalencyValidationContext
by @​dennisdoomen in
fluentassertions/fluentassertions#3242
* Prevent pwn-request RCE via the Qodana workflow by @​dennisdoomen in
fluentassertions/fluentassertions#3217
* Harden NPM install by @​jnyrup in
fluentassertions/fluentassertions#3258
* Pin System.Security.Cryptography.Xml to patched version to fix build
restore failures by @​dennisdoomen in
fluentassertions/fluentassertions#3283
* Exclude dependabot PRs from auto-generated release notes by
@​dennisdoomen in
fluentassertions/fluentassertions#3294
* Fix Qodana PR scans failing on pull requests from forks by
@​dennisdoomen in
fluentassertions/fluentassertions#3299
* Cancel duplicate CI runs when a push triggers both push and
pull_request events by @​dennisdoomen in
fluentassertions/fluentassertions#3298
* Fix Qodana-flagged possible multiple enumeration and unused
auto-property warnings by @​dennisdoomen in
fluentassertions/fluentassertions#3300
* Document lessons learned from #​3291 in agents.md by @​dennisdoomen in
fluentassertions/fluentassertions#3307
* Pin SDK to workaround 10.0.400 by @​jnyrup in
fluentassertions/fluentassertions#3309
* Stop the Build workflow from running twice on same-repo PRs by
@​dennisdoomen in
fluentassertions/fluentassertions#3311
* Update NuGet.Frameworks to workaround Fallout issue by @​jnyrup in
fluentassertions/fluentassertions#3318
* Prefer Count > 0 over Any() by @​jnyrup in
fluentassertions/fluentassertions#3321
* Correct the arguments passed to `string.Format` on failure by @​jnyrup
in fluentassertions/fluentassertions#3325
* Migrate xunit v3 test projects off the VSTest pipeline by
@​dennisdoomen in
fluentassertions/fluentassertions#3320
* Include PackageGuard risk report and SBOM in pipeline by
@​dennisdoomen in
fluentassertions/fluentassertions#3324

## New Contributors
* @​jpgomezv made their first contribution in
fluentassertions/fluentassertions#3317

**Full Changelog**:
fluentassertions/fluentassertions@8.10.0...8.11.0

Commits viewable in [compare
view](fluentassertions/fluentassertions@8.10.0...8.11.0).
</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BeProperSubsetOf and BeProperSupersetOf

3 participants