Skip to content

Commit

Permalink
Prevent multiple enumeration in ContainSingle() (#1753)
Browse files Browse the repository at this point in the history
  • Loading branch information
bert2 committed Dec 1, 2021
1 parent 1aaa84f commit f65fcaf
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
Expand Up @@ -1005,15 +1005,16 @@ public AndConstraint<TAssertions> ContainItemsAssignableTo<TExpectation>(string

if (success)
{
switch (Subject.Count())
ICollection<T> actualItems = Subject.ConvertOrCastToCollection();
switch (actualItems.Count)
{
case 0: // Fail, Collection is empty
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:collection} to contain a single item{reason}, but the collection is empty.");
break;
case 1: // Success Condition
match = Subject.SingleOrDefault();
match = actualItems.Single();
break;
default: // Fail, Collection contains more than a single item
Execute.Assertion
Expand Down
Expand Up @@ -246,6 +246,32 @@ public void When_single_item_is_found_it_should_allow_continuation()
act.Should().Throw<XunitException>().WithMessage(expectedMessage);
}

[Fact]
public void When_collection_is_IEnumerable_it_should_be_evaluated_only_once_with_predicate()
{
// Arrange
IEnumerable<int> collection = new OneTimeEnumerable<int>(1);

// Act
Action act = () => collection.Should().ContainSingle(_ => true);

// Assert
act.Should().NotThrow();
}

[Fact]
public void When_collection_is_IEnumerable_it_should_be_evaluated_only_once()
{
// Arrange
IEnumerable<int> collection = new OneTimeEnumerable<int>(1);

// Act
Action act = () => collection.Should().ContainSingle();

// Assert
act.Should().NotThrow();
}

#endregion
}
}
3 changes: 2 additions & 1 deletion docs/_pages/releases.md
Expand Up @@ -13,6 +13,7 @@ sidebar:
* Adding `ThatAreAsync()` and `ThatAreNotAsync()` for filtering in method assertions - [#1725](https://github.com/fluentassertions/fluentassertions/pull/1725)
* Adding `ThatAreVirtual()` and `ThatAreNotVirtual()` for filtering in method assertions - [#1744](https://github.com/fluentassertions/fluentassertions/pull/1744)
### Fixes
* Prevent multiple enumeration of `IEnumerable`s in parameter-less `ContainSingle()` - [#1753](https://github.com/fluentassertions/fluentassertions/pull/1753)

## 6.2.0

Expand All @@ -27,7 +28,7 @@ sidebar:
* `At` now retains the `DateTimeKind` and keeps sub-second precision when using a `TimeSpan` - [#1687](https://github.com/fluentassertions/fluentassertions/pull/1687).
* Removed iteration over enumerable when generating the `BeEmpty` assertion failure message - [#1692](https://github.com/fluentassertions/fluentassertions/pull/1692).
* Prevent `ArgumentNullException` when formatting a lambda expression containing an extension method - [#1696](https://github.com/fluentassertions/fluentassertions/pull/1696)
* `IgnoringCyclicReferences` in `BeEquivalentTo` now works while comparing value types using `ComparingByMembers` - [#1708](https://github.com/fluentassertions/fluentassertions/pull/1708)
* `IgnoringCyclicReferences` in `BeEquivalentTo` now works while comparing value types using `ComparingByMembers` - [#1708](https://github.com/fluentassertions/fluentassertions/pull/1708)
* Using `BeEquivalentTo` on a collection with nested collections would complain about missing members - [#1713](https://github.com/fluentassertions/fluentassertions/pull/1713)
* Formatting a lambda expression containing lifted operators - [#1714](https://github.com/fluentassertions/fluentassertions/pull/1714).
* Performance improvements in `BeEquivalentTo` by caching expensive Reflection operations - [#1719](https://github.com/fluentassertions/fluentassertions/pull/1719)
Expand Down

0 comments on commit f65fcaf

Please sign in to comment.