diff --git a/Source/aweXpect/That/Collections/CollectionHelpers.cs b/Source/aweXpect/That/Collections/CollectionHelpers.cs index 7e8efec02..e4e19ef62 100644 --- a/Source/aweXpect/That/Collections/CollectionHelpers.cs +++ b/Source/aweXpect/That/Collections/CollectionHelpers.cs @@ -47,15 +47,21 @@ internal static ExpectationBuilder AddCollectionContext(this ExpectationB } return expectationBuilder.UpdateContexts(contexts - => contexts - .Add(new ResultContext("Collection", - () => Formatter.Format(value, typeof(TItem).GetFormattingOption(value switch - { - ICollection coll => coll.Count, - ICountable countable => countable.Count, - _ => null, - })).AppendIsIncomplete(isIncomplete), - -1))); + => + { + if (contexts.All(c => c.Title != "Collection")) + { + contexts + .Add(new ResultContext("Collection", + () => Formatter.Format(value, typeof(TItem).GetFormattingOption(value switch + { + ICollection coll => coll.Count, + ICountable countable => countable.Count, + _ => null, + })).AppendIsIncomplete(isIncomplete), + -1)); + } + }); } internal static ExpectationBuilder AddCollectionContext(this ExpectationBuilder expectationBuilder, @@ -77,15 +83,21 @@ internal static ExpectationBuilder AddCollectionContext(this ExpectationBuilder } return expectationBuilder.UpdateContexts(contexts - => contexts - .Add(new ResultContext("Collection", - () => Formatter.Format(value, type.GetFormattingOption(value switch - { - ICollection coll => coll.Count, - ICountable countable => countable.Count, - _ => null, - })).AppendIsIncomplete(isIncomplete), - -1))); + => + { + if (contexts.All(c => c.Title != "Collection")) + { + contexts + .Add(new ResultContext("Collection", + () => Formatter.Format(value, type.GetFormattingOption(value switch + { + ICollection coll => coll.Count, + ICountable countable => countable.Count, + _ => null, + })).AppendIsIncomplete(isIncomplete), + -1)); + } + }); } #if NET8_0_OR_GREATER @@ -98,12 +110,18 @@ internal static ExpectationBuilder AddCollectionContext(this ExpectationB } return expectationBuilder.UpdateContexts(contexts - => contexts - .Add(new ResultContext("Collection", - () => Formatter.Format(value.MaterializedItems, - typeof(TItem).GetFormattingOption(value.Count)) - .AppendIsIncomplete(isIncomplete), - -1))); + => + { + if (contexts.All(c => c.Title != "Collection")) + { + contexts + .Add(new ResultContext("Collection", + () => Formatter.Format(value.MaterializedItems, + typeof(TItem).GetFormattingOption(value.Count)) + .AppendIsIncomplete(isIncomplete), + -1)); + } + }); } #endif @@ -116,11 +134,17 @@ internal static ExpectationBuilder AddCollectionContext(this Expec } return expectationBuilder.UpdateContexts(contexts - => contexts - .Add(new ResultContext("Dictionary", - () => Formatter.Format(value, typeof(TValue).GetFormattingOption(value.Count)) - .AppendIsIncomplete(isIncomplete), - -2))); + => + { + if (contexts.All(c => c.Title != "Dictionary")) + { + contexts + .Add(new ResultContext("Dictionary", + () => Formatter.Format(value, typeof(TValue).GetFormattingOption(value.Count)) + .AppendIsIncomplete(isIncomplete), + -2)); + } + }); } internal static ExpectationBuilder AddCollectionContext(this ExpectationBuilder expectationBuilder, @@ -132,11 +156,17 @@ internal static ExpectationBuilder AddCollectionContext(this Expec } return expectationBuilder.UpdateContexts(contexts - => contexts - .Add(new ResultContext("Dictionary", - () => Formatter.Format(value, typeof(TValue).GetFormattingOption(value.Count)) - .AppendIsIncomplete(isIncomplete), - -2))); + => + { + if (contexts.All(c => c.Title != "Dictionary")) + { + contexts + .Add(new ResultContext("Dictionary", + () => Formatter.Format(value, typeof(TValue).GetFormattingOption(value.Count)) + .AppendIsIncomplete(isIncomplete), + -2)); + } + }); } internal static string AppendIsIncomplete(this string formattedItems, bool isIncomplete) diff --git a/Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.Contains.Tests.cs b/Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.Contains.Tests.cs index 9dc24989a..9961d45ef 100644 --- a/Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.Contains.Tests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.Contains.Tests.cs @@ -339,6 +339,29 @@ Expected that subject but it was """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + IAsyncEnumerable subject = ToAsyncEnumerable(["a", "b", "c",]); + + async Task Act() + => await That(subject).Contains("d").And.Contains("e"); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains "d" at least once and contains "e" at least once, + but it did not contain it + + Collection: + [ + "a", + "b", + "c" + ] + """); + } } public sealed class StringItemTests diff --git a/Tests/aweXpect.Tests/Collections/ThatDictionary.ContainsKey.Tests.cs b/Tests/aweXpect.Tests/Collections/ThatDictionary.ContainsKey.Tests.cs index 97fc3eeb6..2e797d26e 100644 --- a/Tests/aweXpect.Tests/Collections/ThatDictionary.ContainsKey.Tests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatDictionary.ContainsKey.Tests.cs @@ -144,6 +144,33 @@ Expected that subject but it was """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + IDictionary subject = ToDictionary([1, 2, 3,], ["foo", "bar", "baz",]); + + async Task Act() + => await That(subject).ContainsKey(4).And.ContainsKey(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains key 4 and contains key 5, + but it contained only [ + 1, + 2, + 3 + ] + + Dictionary: + { + [1] = "foo", + [2] = "bar", + [3] = "baz" + } + """); + } } } } diff --git a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.EnumerableTests.cs b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.EnumerableTests.cs index db72b3638..17a2a6aa3 100644 --- a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.EnumerableTests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.EnumerableTests.cs @@ -373,6 +373,25 @@ Expected that subject but it was """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + IEnumerable subject = ToEnumerable([1, 2, 3,]); + + async Task Act() + => await That(subject).Contains(4).And.Contains(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains 4 at least once and contains 5 at least once, + but it did not contain it + + Collection: + [1, 2, 3] + """); + } } public sealed class EnumerablePredicateTests diff --git a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.ImmutableTests.cs b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.ImmutableTests.cs index 6961cc443..4419a3d9d 100644 --- a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.ImmutableTests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.ImmutableTests.cs @@ -372,6 +372,29 @@ but it did not contain it {Formatter.Format(subject)} """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + ImmutableArray subject = ["a", "b", "c",]; + + async Task Act() + => await That(subject).Contains("d").And.Contains("e"); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains "d" at least once and contains "e" at least once, + but it did not contain it + + Collection: + [ + "a", + "b", + "c" + ] + """); + } } public sealed class ImmutableStringItemTests diff --git a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.Tests.cs b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.Tests.cs index 908a8259d..c554811d3 100644 --- a/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.Tests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatEnumerable.Contains.Tests.cs @@ -417,6 +417,29 @@ Expected that subject but it was """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + IEnumerable subject = ToEnumerable(["a", "b", "c",]); + + async Task Act() + => await That(subject).Contains("d").And.Contains("e"); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains "d" at least once and contains "e" at least once, + but it did not contain it + + Collection: + [ + "a", + "b", + "c" + ] + """); + } } public sealed class StringItemTests diff --git a/Tests/aweXpect.Tests/Collections/ThatReadOnlyDictionary.ContainsKey.Tests.cs b/Tests/aweXpect.Tests/Collections/ThatReadOnlyDictionary.ContainsKey.Tests.cs index e738615b6..35b32d5de 100644 --- a/Tests/aweXpect.Tests/Collections/ThatReadOnlyDictionary.ContainsKey.Tests.cs +++ b/Tests/aweXpect.Tests/Collections/ThatReadOnlyDictionary.ContainsKey.Tests.cs @@ -145,6 +145,33 @@ Expected that subject but it was """); } + + [Fact] + public async Task WithMultipleFailures_ShouldIncludeCollectionOnlyOnce() + { + IReadOnlyDictionary subject = ToDictionary([1, 2, 3,], ["foo", "bar", "baz",]); + + async Task Act() + => await That(subject).ContainsKey(4).And.ContainsKey(5); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + contains key 4 and contains key 5, + but it contained only [ + 1, + 2, + 3 + ] + + Dictionary: + { + [1] = "foo", + [2] = "bar", + [3] = "baz" + } + """); + } } } }