From 03aa90e0c4651768eeae960ae13fec0c2cf66f80 Mon Sep 17 00:00:00 2001 From: Muller Date: Mon, 3 Jan 2022 15:20:52 -0300 Subject: [PATCH 1/8] Fixed ContainItemsAssignableTo behaviour, now expects at least one element of specified type. --- .../GenericCollectionAssertions.cs | 26 ++++----- ...AssertionSpecs.ContainItemsAssignableTo.cs | 58 +++++++------------ 2 files changed, 32 insertions(+), 52 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index ff75c7090d..0ba37176dd 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -965,20 +965,18 @@ public AndConstraint ContainItemsAssignableTo(string if (success) { - int index = 0; - foreach (T item in Subject) - { - if (item is not TExpectation) - { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:collection} to contain only items of type {0}{reason}" + - ", but item {1} at index {2} is of type {3}.", typeof(TExpectation), item, index, item.GetType()); - } - - ++index; - } + Execute.Assertion + .BecauseOf(because, becauseArgs) + .WithExpectation("Expected {context:collection} to contain element assignable to type {0}{reason}, ", typeof(TExpectation).FullName) + .Given(() => Subject) + .ForCondition(subject => subject.Any()) + .FailWith("but was empty.") + .Then + .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", + subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]") + .Then + .ClearExpectation(); } return new AndConstraint((TAssertions)this); diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs index 524608e968..64e71030b0 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs @@ -24,7 +24,7 @@ public void Should_succeed_when_asserting_collection_with_all_items_of_same_type } [Fact] - public void Should_fail_when_asserting_collection_with_items_of_different_types_only_contains_item_of_one_type() + public void Should_succeed_when_asserting_collection_with_items_of_different_types_only_contains_item_of_one_type() { // Arrange var collection = new List @@ -33,68 +33,50 @@ public void Should_fail_when_asserting_collection_with_items_of_different_types_ "2" }; - // Act - Action act = () => collection.Should().ContainItemsAssignableTo(); - - // Assert - act.Should().Throw(); + // Act / Assert + collection.Should().ContainItemsAssignableTo(); } [Fact] - public void When_a_collection_contains_anything_other_than_strings_it_should_throw_and_report_details() + public void When_asserting_collection_contains_item_assignable_to_against_null_collection_it_should_throw() { // Arrange - var collection = new List - { - 1, - "2" - }; + int[] collection = null; // Act - Action act = () => collection.Should().ContainItemsAssignableTo(); + Action act = () => + { + using var _ = new AssertionScope(); + collection.Should().ContainItemsAssignableTo("because we want to test the behaviour with a null subject"); + }; // Assert act.Should().Throw().WithMessage( - "Expected collection to contain only items of type System.String, but item 1 at index 0 is of type System.Int32."); + "Expected collection to contain element assignable to type System.String because we want to test the behaviour with a null subject, but found ."); } [Fact] - public void When_a_collection_contains_anything_other_than_strings_it_should_use_the_reason() + public void When_a_collection_is_empty_an_exception_should_be_thrown() { // Arrange - var collection = new List - { - 1, - "2" - }; + int[] collection = Array.Empty(); // Act - Action act = () => collection.Should().ContainItemsAssignableTo( - "because we want to test the failure {0}", "message"); + Action act = () => collection.Should().ContainItemsAssignableTo(); // Assert - act.Should().Throw() - .WithMessage( - "Expected collection to contain only items of type System.String because we want to test the failure message" + - ", but item 1 at index 0 is of type System.Int32."); + act.Should().Throw().WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but was empty."); } [Fact] - public void When_asserting_collection_contains_item_assignable_to_against_null_collection_it_should_throw() + public void Should_throw_exception_when_asserting_collection_for_missing_item_type() { - // Arrange - int[] collection = null; + var collection = new object[] { "1", 1.0m }; - // Act - Action act = () => - { - using var _ = new AssertionScope(); - collection.Should().ContainItemsAssignableTo("because we want to test the behaviour with a null subject"); - }; + Action act = () => collection.Should().ContainItemsAssignableTo(); - // Assert - act.Should().Throw().WithMessage( - "Expected collection to contain element assignable to type System.String because we want to test the behaviour with a null subject, but found ."); + act.Should().Throw() + .WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found \"[System.String, System.Decimal]\"."); } #endregion From 99cc1bc7fd8bba2dcee25ab031f062f3e39f22dd Mon Sep 17 00:00:00 2001 From: Muller Date: Mon, 3 Jan 2022 15:38:45 -0300 Subject: [PATCH 2/8] Updated release notes. --- docs/_pages/releases.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index efad6be024..23b2ac79fc 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -12,6 +12,7 @@ sidebar: ### What's New ### Fixes +* `ContainItemsAssignableTo` Now Expects At Least One Item Assignable to `T` - [#1765](https://github.com/fluentassertions/fluentassertions/pull/1765) ## 6.3.0 From 4351910de91284651055295ae2d972cfd623cbba Mon Sep 17 00:00:00 2001 From: Leandro Muller Date: Thu, 6 Jan 2022 17:35:37 -0300 Subject: [PATCH 3/8] Update Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs Co-authored-by: Jonas Nyrup --- .../CollectionAssertionSpecs.ContainItemsAssignableTo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs index 64e71030b0..134e7ca3bc 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs @@ -24,7 +24,7 @@ public void Should_succeed_when_asserting_collection_with_all_items_of_same_type } [Fact] - public void Should_succeed_when_asserting_collection_with_items_of_different_types_only_contains_item_of_one_type() + public void Should_succeed_when_asserting_collection_with_items_of_different_types_contains_item_of_expected_type() { // Arrange var collection = new List From f4f87bad178ba8552c47743844ec0b35e61807af Mon Sep 17 00:00:00 2001 From: Muller Date: Thu, 6 Jan 2022 19:20:54 -0300 Subject: [PATCH 4/8] Changes requested. No longer enumerating subject. No longer manually formatting types on assertion failure. Lower case release notes. Changed XML DOC for affected methods. Unrelated changes regarding invalid XML DOCs for other methods. --- .../Collections/GenericCollectionAssertions.cs | 11 +++++------ Src/FluentAssertions/Common/Configuration.cs | 2 +- Src/FluentAssertions/Execution/GivenSelector.cs | 2 +- ...llectionAssertionSpecs.ContainItemsAssignableTo.cs | 2 +- docs/_pages/releases.md | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index 0ba37176dd..fc6ea3a145 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -946,7 +946,7 @@ public AndConstraint ContainInOrder(IEnumerable expected, string } /// - /// Asserts that the current collection only contains items that are assignable to the type . + /// Asserts that the current collection contains items that are assignable to the type . /// /// /// A formatted phrase as is supported by explaining why the assertion @@ -965,16 +965,15 @@ public AndConstraint ContainItemsAssignableTo(string if (success) { + var actualItems = Subject.ConvertOrCastToCollection(); Execute.Assertion .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:collection} to contain element assignable to type {0}{reason}, ", typeof(TExpectation).FullName) - .Given(() => Subject) - .ForCondition(subject => subject.Any()) + .ForCondition(actualItems.Any()) .FailWith("but was empty.") .Then - .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", - subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]") + .ForCondition(actualItems.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", actualItems.Select(x => x.GetType())) .Then .ClearExpectation(); } diff --git a/Src/FluentAssertions/Common/Configuration.cs b/Src/FluentAssertions/Common/Configuration.cs index a59e40be46..d46724dd7e 100644 --- a/Src/FluentAssertions/Common/Configuration.cs +++ b/Src/FluentAssertions/Common/Configuration.cs @@ -76,7 +76,7 @@ private ValueFormatterDetectionMode DetermineFormatterDetectionMode() /// /// Gets or sets the assembly name to scan for custom value formatters in case - /// is set to . + /// is set to . /// public string ValueFormatterAssembly { diff --git a/Src/FluentAssertions/Execution/GivenSelector.cs b/Src/FluentAssertions/Execution/GivenSelector.cs index 395e2aeb12..58d46f2647 100644 --- a/Src/FluentAssertions/Execution/GivenSelector.cs +++ b/Src/FluentAssertions/Execution/GivenSelector.cs @@ -49,7 +49,7 @@ public GivenSelector ForCondition(Func predicate) /// The will not be invoked if the prior assertion failed, /// nor will throw any exceptions. /// - /// + /// public GivenSelector Given(Func selector) { Guard.ThrowIfArgumentIsNull(selector, nameof(selector)); diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs index 134e7ca3bc..302f3ffc7b 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs @@ -76,7 +76,7 @@ public void Should_throw_exception_when_asserting_collection_for_missing_item_ty Action act = () => collection.Should().ContainItemsAssignableTo(); act.Should().Throw() - .WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found \"[System.String, System.Decimal]\"."); + .WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found {System.String, System.Decimal}."); } #endregion diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index 23b2ac79fc..4c67349a82 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -12,7 +12,7 @@ sidebar: ### What's New ### Fixes -* `ContainItemsAssignableTo` Now Expects At Least One Item Assignable to `T` - [#1765](https://github.com/fluentassertions/fluentassertions/pull/1765) +* `ContainItemsAssignableTo` now expects at least one item assignable to `T` - [#1765](https://github.com/fluentassertions/fluentassertions/pull/1765) ## 6.3.0 From 60a889020c7cb1829de3e9a811536b4ed96b22ce Mon Sep 17 00:00:00 2001 From: Muller Date: Fri, 7 Jan 2022 13:03:58 -0300 Subject: [PATCH 5/8] Removed `any()` enumeration, only one assertion was necessary. --- .../Collections/GenericCollectionAssertions.cs | 7 +------ .../CollectionAssertionSpecs.ContainItemsAssignableTo.cs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index fc6ea3a145..23eb1ada90 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -969,13 +969,8 @@ public AndConstraint ContainItemsAssignableTo(string Execute.Assertion .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:collection} to contain element assignable to type {0}{reason}, ", typeof(TExpectation).FullName) - .ForCondition(actualItems.Any()) - .FailWith("but was empty.") - .Then .ForCondition(actualItems.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", actualItems.Select(x => x.GetType())) - .Then - .ClearExpectation(); + .FailWith("but found {0}.", actualItems.Select(x => x.GetType())); } return new AndConstraint((TAssertions)this); diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs index 302f3ffc7b..d7a6ee706a 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs @@ -65,7 +65,7 @@ public void When_a_collection_is_empty_an_exception_should_be_thrown() Action act = () => collection.Should().ContainItemsAssignableTo(); // Assert - act.Should().Throw().WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but was empty."); + act.Should().Throw().WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found {empty}."); } [Fact] From 3e7255089ab330db576c5c91d5a63daf8f10611a Mon Sep 17 00:00:00 2001 From: Muller Date: Fri, 7 Jan 2022 14:21:38 -0300 Subject: [PATCH 6/8] Refactored code to merge into a single, simpler Assertion. Changed impacted unit tests. --- .../GenericCollectionAssertions.cs | 23 ++++++++----------- ...AssertionSpecs.ContainItemsAssignableTo.cs | 6 ++--- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index 23eb1ada90..dc16b15747 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -946,7 +946,7 @@ public AndConstraint ContainInOrder(IEnumerable expected, string } /// - /// Asserts that the current collection contains items that are assignable to the type . + /// Asserts that the current collection contains at least one element that is assignable to the type . /// /// /// A formatted phrase as is supported by explaining why the assertion @@ -957,21 +957,16 @@ public AndConstraint ContainInOrder(IEnumerable expected, string /// public AndConstraint ContainItemsAssignableTo(string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + Execute.Assertion .BecauseOf(because, becauseArgs) + .WithExpectation("Expected {context:collection} to contain at least one element assignable to type {0}{reason}, ", + typeof(TExpectation).FullName) .ForCondition(Subject is not null) - .FailWith("Expected {context:collection} to contain element assignable to type {0}{reason}, but found .", - typeof(TExpectation)); - - if (success) - { - var actualItems = Subject.ConvertOrCastToCollection(); - Execute.Assertion - .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain element assignable to type {0}{reason}, ", typeof(TExpectation).FullName) - .ForCondition(actualItems.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", actualItems.Select(x => x.GetType())); - } + .FailWith("but found .") + .Then + .Given(() => Subject.ConvertOrCastToCollection()) + .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", subject => subject.Select(x => x.GetType())); return new AndConstraint((TAssertions)this); } diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs index d7a6ee706a..43f3518c2a 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainItemsAssignableTo.cs @@ -52,7 +52,7 @@ public void When_asserting_collection_contains_item_assignable_to_against_null_c // Assert act.Should().Throw().WithMessage( - "Expected collection to contain element assignable to type System.String because we want to test the behaviour with a null subject, but found ."); + "Expected collection to contain at least one element assignable to type \"System.String\" because we want to test the behaviour with a null subject, but found ."); } [Fact] @@ -65,7 +65,7 @@ public void When_a_collection_is_empty_an_exception_should_be_thrown() Action act = () => collection.Should().ContainItemsAssignableTo(); // Assert - act.Should().Throw().WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found {empty}."); + act.Should().Throw().WithMessage("Expected collection to contain at least one element assignable to type \"System.Int32\", but found {empty}."); } [Fact] @@ -76,7 +76,7 @@ public void Should_throw_exception_when_asserting_collection_for_missing_item_ty Action act = () => collection.Should().ContainItemsAssignableTo(); act.Should().Throw() - .WithMessage("Expected collection to contain element assignable to type \"System.Int32\", but found {System.String, System.Decimal}."); + .WithMessage("Expected collection to contain at least one element assignable to type \"System.Int32\", but found {System.String, System.Decimal}."); } #endregion From b0afd36b3c1f667250ef0f08dd19fb0107987154 Mon Sep 17 00:00:00 2001 From: Muller Date: Sat, 8 Jan 2022 13:47:16 -0300 Subject: [PATCH 7/8] Ending method chain in ClearExpectations. --- .../Collections/GenericCollectionAssertions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index dc16b15747..77bfa479ca 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -966,7 +966,9 @@ public AndConstraint ContainItemsAssignableTo(string .Then .Given(() => Subject.ConvertOrCastToCollection()) .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", subject => subject.Select(x => x.GetType())); + .FailWith("but found {0}.", subject => subject.Select(x => x.GetType())) + .Then + .ClearExpectation(); return new AndConstraint((TAssertions)this); } From 65a30b1aa34d735173589b02dc188542ac3f6b51 Mon Sep 17 00:00:00 2001 From: Muller Date: Sun, 9 Jan 2022 10:05:02 -0300 Subject: [PATCH 8/8] FailWith now uses GetType(x) instead of x.GetType() --- Src/FluentAssertions/Collections/GenericCollectionAssertions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index 77bfa479ca..54a10c802c 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -966,7 +966,7 @@ public AndConstraint ContainItemsAssignableTo(string .Then .Given(() => Subject.ConvertOrCastToCollection()) .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", subject => subject.Select(x => x.GetType())) + .FailWith("but found {0}.", subject => subject.Select(x => GetType(x))) .Then .ClearExpectation();