From ae9e230c5958a050df020644201887500e4efc3e Mon Sep 17 00:00:00 2001 From: Muiz Atolagbe Date: Tue, 14 Oct 2025 00:36:54 +0100 Subject: [PATCH 1/4] implemented non-generic overload for Assert.ContainsSingle --- .../Assertions/Assert.Contains.cs | 196 ++++++++++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 10 + .../Assertions/AssertTests.Contains.cs | 76 +++++++ 3 files changed, 282 insertions(+) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs index 1264810181..ae56aeef78 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs @@ -89,6 +89,98 @@ public void AppendFormatted(object? value, int alignment = 0, string? format = n #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public readonly struct AssertSingleInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly int _actualCount; + private readonly object? _item; + + public AssertSingleInterpolatedStringHandler(int literalLength, int formattedCount, IEnumerable collection, out bool shouldAppend) + { + _actualCount = 0; + object? firstItem = null; + + foreach (object? item in collection) + { + if (_actualCount == 0) + { + firstItem = item; + } + + _actualCount++; + + if (_actualCount > 1) + { + break; + } + } + + shouldAppend = _actualCount != 1; + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + else + { + _item = firstItem; + } + } + + internal object ComputeAssertion(string collectionExpression) + { + if (_builder is not null) + { + _builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "collection", collectionExpression) + " "); + ThrowAssertContainsSingleFailed(_actualCount, _builder.ToString()); + } + + return _item!; + } + + public void AppendLiteral(string value) + => _builder!.Append(value); + + public void AppendFormatted(T value) + => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) + => _builder!.Append(value); + + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + => AppendFormatted(value.ToString(), alignment, format); +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) + => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) + => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) + => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) + => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public void AppendFormatted(string? value, int alignment = 0, string? format = null) + => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) + => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + /// /// Tests whether the specified collection contains exactly one element. /// @@ -105,6 +197,21 @@ public static T ContainsSingle(IEnumerable collection, [InterpolatedString #pragma warning restore IDE0060 // Remove unused parameter => message.ComputeAssertion(collectionExpression); + /// + /// Tests whether the specified collection contains exactly one element. + /// + /// The collection. + /// The message to display when the assertion fails. + /// + /// The syntactic expression of collection as given by the compiler via caller argument expression. + /// Users shouldn't pass a value for this parameter. + /// + /// The item. +#pragma warning disable IDE0060 // Remove unused parameter + public static object ContainsSingle(IEnumerable collection, [InterpolatedStringHandlerArgument(nameof(collection))] ref AssertSingleInterpolatedStringHandler message, [CallerArgumentExpression(nameof(collection))] string collectionExpression = "") +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(collectionExpression); + #pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads #region ContainsSingle @@ -135,6 +242,49 @@ public static T ContainsSingle(IEnumerable collection, string? message = " return default; } + /// + /// Tests whether the specified collection contains exactly one element. + /// + /// The collection. + /// The message to display when the assertion fails. + /// + /// The syntactic expression of collection as given by the compiler via caller argument expression. + /// Users shouldn't pass a value for this parameter. + /// + /// The item. + public static object ContainsSingle(IEnumerable collection, string? message = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "") + { + int actualCount = 0; + object? firstItem = null; + + foreach (object? item in collection) + { + if (actualCount == 0) + { + firstItem = item; + } + + actualCount++; + + // Early exit if we already know there's more than one item + if (actualCount > 1) + { + break; + } + } + + if (actualCount == 1) + { + return firstItem!; + } + + string userMessage = BuildUserMessageForCollectionExpression(message, collectionExpression); + ThrowAssertContainsSingleFailed(actualCount, userMessage); + + // Unreachable code but compiler cannot work it out + return default!; + } + /// /// Tests whether the specified collection contains exactly one element that matches the given predicate. /// @@ -168,6 +318,52 @@ public static T ContainsSingle(Func predicate, IEnumerable collec return default; } + /// + /// Tests whether the specified collection contains exactly one element that matches the given predicate. + /// + /// A function to test each element for a condition. + /// The collection. + /// The message to display when the assertion fails. + /// + /// The syntactic expression of predicate as given by the compiler via caller argument expression. + /// Users shouldn't pass a value for this parameter. + /// + /// + /// The syntactic expression of collection as given by the compiler via caller argument expression. + /// Users shouldn't pass a value for this parameter. + /// + /// The item that matches the predicate. + public static object ContainsSingle(Func predicate, IEnumerable collection, string? message = "", [CallerArgumentExpression(nameof(predicate))] string predicateExpression = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "") + { + var matchingElements = new List(); + + foreach (object? item in collection) + { + if (predicate(item)) + { + matchingElements.Add(item); + + // Early exit optimization - no need to continue if we already have more than one match + if (matchingElements.Count > 1) + { + break; + } + } + } + + int actualCount = matchingElements.Count; + if (actualCount == 1) + { + return matchingElements[0]; + } + + string userMessage = BuildUserMessageForPredicateExpressionAndCollectionExpression(message, predicateExpression, collectionExpression); + ThrowAssertSingleMatchFailed(actualCount, userMessage); + + // Unreachable code but compiler cannot work it out + return default!; + } + #endregion // ContainsSingle #region Contains diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 7dc5c58110..58fe50fdd0 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,11 @@ +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendLiteral(string value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AssertSingleInterpolatedStringHandler(int literalLength, int formattedCount, System.Collections.IEnumerable collection, out bool shouldAppend) -> void + #nullable enable diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs index 39b830c8cd..12c597f5e8 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs @@ -279,6 +279,21 @@ public void ContainsSingle_NoMessage_WithSingleElement_ReturnsElement() result.Should().Be(100); } + /// + /// Tests the ContainsSingle method without message parameters where the collection has a single element. + /// + public void ContainsSingle_InNonGenericCollection_NoMessage_WithSingleElement_ReturnsElement() + { + // Arrange + var collection = new ArrayList { 100 }; + + // Act + object? result = Assert.ContainsSingle(collection); + + // Assert + result.Should().Be(100); + } + /// /// Tests the ContainsSingle method with a message where the collection has a single element. /// @@ -294,6 +309,21 @@ public void ContainsSingle_WithMessage_WithSingleElement_ReturnsElement() result.Should().Be("OnlyOne"); } + /// + /// Tests the ContainsSingle method with a message where the collection has a single element. + /// + public void ContainsSingle_InNonGenericCollection_WithMessage_WithSingleElement_ReturnsElement() + { + // Arrange + var collection = new ArrayList { "OnlyOne" }; + + // Act + object? result = Assert.ContainsSingle(collection, "Custom message"); + + // Assert + result.Should().Be("OnlyOne"); + } + /// /// Tests the ContainsSingle method that uses an interpolated string handler when the collection has multiple elements. /// Expects an exception. @@ -311,6 +341,52 @@ public void ContainsSingle_InterpolatedHandler_WithMultipleElements_ThrowsExcept action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). 'collection' expression: 'collection'. "); } + /// + /// Tests the ContainsSingle method that uses an interpolated string handler when the collection has multiple elements. + /// Expects an exception. + /// + public void ContainsSingle_InNonGenericCollection_InterpolatedHandler_WithMultipleElements_ThrowsException() + { + // Arrange + var collection = new ArrayList { 1, 2, 3 }; + var handler = new Assert.AssertSingleInterpolatedStringHandler(literalLength: 5, formattedCount: 0, collection, out bool shouldAppend); + + // Act + Action action = () => Assert.ContainsSingle(collection, ref handler); + + // Assert + action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 2 element(s). 'collection' expression: 'collection'. "); + } + + /// + /// Tests the ContainsSingle method without message parameters where the collection has a single element. + /// + public void ContainsSingle_InNonGenericCollection_NoMessage_WithNull_ReturnsElement() + { + // Arrange + var collection = new ArrayList { null }; + + // Act + object? result = Assert.ContainsSingle(collection); + + // Assert + result.Should().Be(null); + } + + /// + /// Tests the ContainsSingle method without message parameters where the collection has a single element. + /// + public void ContainsSingle_InNonGenericCollection_NoMessage_WithEmptyCollection_ReturnsNoElement() + { + // Arrange + var collection = new ArrayList(); + + // Act + Action action = () => Assert.ContainsSingle(collection); + + // Assert + action.Should().Throw(); + } #endregion #region Contains Tests From 33ca78a909fde097b5d3ffef3c900aac6b6b828d Mon Sep 17 00:00:00 2001 From: Muiz Atolagbe Date: Tue, 14 Oct 2025 01:11:54 +0100 Subject: [PATCH 2/4] modified the publicAPI.unshipped --- .../TestFramework/PublicAPI/PublicAPI.Unshipped.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 58fe50fdd0..d9d547fad1 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +#nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void @@ -7,5 +8,3 @@ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStri Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendLiteral(string value) -> void Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AssertSingleInterpolatedStringHandler(int literalLength, int formattedCount, System.Collections.IEnumerable collection, out bool shouldAppend) -> void - -#nullable enable From 630887f45b60f165f1002d8a12b246bbb88e0375 Mon Sep 17 00:00:00 2001 From: Muiz Atolagbe Date: Tue, 14 Oct 2025 22:45:58 +0100 Subject: [PATCH 3/4] modified the assert.containssinlge by eliminated the AssertSingleInterpolationStringHandler based off Assert.HasCount --- .../Assertions/Assert.Contains.cs | 107 ------------------ .../PublicAPI/PublicAPI.Unshipped.txt | 11 +- .../Assertions/AssertTests.Contains.cs | 17 --- 3 files changed, 2 insertions(+), 133 deletions(-) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs index ae56aeef78..c663dd0307 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs @@ -89,98 +89,6 @@ public void AppendFormatted(object? value, int alignment = 0, string? format = n #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters - [InterpolatedStringHandler] - [EditorBrowsable(EditorBrowsableState.Never)] -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public readonly struct AssertSingleInterpolatedStringHandler - { - private readonly StringBuilder? _builder; - private readonly int _actualCount; - private readonly object? _item; - - public AssertSingleInterpolatedStringHandler(int literalLength, int formattedCount, IEnumerable collection, out bool shouldAppend) - { - _actualCount = 0; - object? firstItem = null; - - foreach (object? item in collection) - { - if (_actualCount == 0) - { - firstItem = item; - } - - _actualCount++; - - if (_actualCount > 1) - { - break; - } - } - - shouldAppend = _actualCount != 1; - if (shouldAppend) - { - _builder = new StringBuilder(literalLength + formattedCount); - } - else - { - _item = firstItem; - } - } - - internal object ComputeAssertion(string collectionExpression) - { - if (_builder is not null) - { - _builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionSingleParameterMessage, "collection", collectionExpression) + " "); - ThrowAssertContainsSingleFailed(_actualCount, _builder.ToString()); - } - - return _item!; - } - - public void AppendLiteral(string value) - => _builder!.Append(value); - - public void AppendFormatted(T value) - => AppendFormatted(value, format: null); - -#if NETCOREAPP3_1_OR_GREATER - public void AppendFormatted(ReadOnlySpan value) - => _builder!.Append(value); - - public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) - => AppendFormatted(value.ToString(), alignment, format); -#endif - - // NOTE: All the overloads involving format and/or alignment are not super efficient. - // This code path is only for when an assert is failing, so that's not the common scenario - // and should be okay if not very optimized. - // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to - // the BCL's StringBuilder.AppendInterpolatedStringHandler - public void AppendFormatted(T value, string? format) - => _builder!.AppendFormat(null, $"{{0:{format}}}", value); - - public void AppendFormatted(T value, int alignment) - => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); - - public void AppendFormatted(T value, int alignment, string? format) - => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); - - public void AppendFormatted(string? value) - => _builder!.Append(value); - -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters - public void AppendFormatted(string? value, int alignment = 0, string? format = null) - => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); - - public void AppendFormatted(object? value, int alignment = 0, string? format = null) - => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters - } -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - /// /// Tests whether the specified collection contains exactly one element. /// @@ -197,21 +105,6 @@ public static T ContainsSingle(IEnumerable collection, [InterpolatedString #pragma warning restore IDE0060 // Remove unused parameter => message.ComputeAssertion(collectionExpression); - /// - /// Tests whether the specified collection contains exactly one element. - /// - /// The collection. - /// The message to display when the assertion fails. - /// - /// The syntactic expression of collection as given by the compiler via caller argument expression. - /// Users shouldn't pass a value for this parameter. - /// - /// The item. -#pragma warning disable IDE0060 // Remove unused parameter - public static object ContainsSingle(IEnumerable collection, [InterpolatedStringHandlerArgument(nameof(collection))] ref AssertSingleInterpolatedStringHandler message, [CallerArgumentExpression(nameof(collection))] string collectionExpression = "") -#pragma warning restore IDE0060 // Remove unused parameter - => message.ComputeAssertion(collectionExpression); - #pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads #region ContainsSingle diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index d9d547fad1..0a398540b2 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,10 +1,3 @@ #nullable enable -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AppendLiteral(string value) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertSingleInterpolatedStringHandler.AssertSingleInterpolatedStringHandler(int literalLength, int formattedCount, System.Collections.IEnumerable collection, out bool shouldAppend) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Collections.IEnumerable! collection, string? message = "", string! collectionExpression = "") -> object! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ContainsSingle(System.Func! predicate, System.Collections.IEnumerable! collection, string? message = "", string! predicateExpression = "", string! collectionExpression = "") -> object! diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs index 12c597f5e8..76121cfa09 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs @@ -341,23 +341,6 @@ public void ContainsSingle_InterpolatedHandler_WithMultipleElements_ThrowsExcept action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 3 element(s). 'collection' expression: 'collection'. "); } - /// - /// Tests the ContainsSingle method that uses an interpolated string handler when the collection has multiple elements. - /// Expects an exception. - /// - public void ContainsSingle_InNonGenericCollection_InterpolatedHandler_WithMultipleElements_ThrowsException() - { - // Arrange - var collection = new ArrayList { 1, 2, 3 }; - var handler = new Assert.AssertSingleInterpolatedStringHandler(literalLength: 5, formattedCount: 0, collection, out bool shouldAppend); - - // Act - Action action = () => Assert.ContainsSingle(collection, ref handler); - - // Assert - action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 2 element(s). 'collection' expression: 'collection'. "); - } - /// /// Tests the ContainsSingle method without message parameters where the collection has a single element. /// From 90c6362f79d2e86733a7941b147cdcc15295efdf Mon Sep 17 00:00:00 2001 From: Muiz Atolagbe Date: Fri, 24 Oct 2025 22:51:09 +0100 Subject: [PATCH 4/4] added an additional test case that assert actual custom message from exception thrown --- .../Assertions/AssertTests.Contains.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs index 76121cfa09..c49b23ba27 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.Contains.cs @@ -370,6 +370,21 @@ public void ContainsSingle_InNonGenericCollection_NoMessage_WithEmptyCollection_ // Assert action.Should().Throw(); } + + /// + /// Tests the ContainsSingle method without message parameters where the collection has a no element (empty collection). + /// + public void ContainsSingle_InNonGenericCollection_AssertCustomMessage_WithEmptyCollection_ThrowsException() + { + // Arrange + var collection = new ArrayList(); + + // Act + Action action = () => Assert.ContainsSingle(collection); + + // Assert + action.Should().Throw().WithMessage("Assert.ContainsSingle failed. Expected collection to contain exactly one element but found 0 element(s). 'collection' expression: 'collection'."); + } #endregion #region Contains Tests