From 3872793f0ec1045482c8899923cc0be77cda9206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2026 15:49:13 +0200 Subject: [PATCH 1/2] Update That property XML docs to link to dotnet/docs assertions guide Replaces the broken testfx README link in the of Assert.That, StringAssert.That, and CollectionAssert.That with deep links to the new MSTest assertions documentation: - Assert.That -> 'Create custom assertions with Assert.That' section - StringAssert.That / CollectionAssert.That -> 'Extension hooks on StringAssert and CollectionAssert' subsection Also clarifies that for new custom assertions, users should extend Assert.That rather than the StringAssert.That / CollectionAssert.That hooks, since StringAssert and CollectionAssert are likely to be deprecated. Related: https://github.com/dotnet/docs/pull/54185 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TestFramework/TestFramework/Assertions/Assert.cs | 6 +++--- .../TestFramework/Assertions/CollectionAssert.cs | 11 ++++++++--- .../TestFramework/Assertions/StringAssert.cs | 11 ++++++++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index b345210c39..aa16dfab46 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -20,9 +20,9 @@ private Assert() /// /// /// Users can use this to plug-in custom assertions through C# extension methods. - /// For instance, the signature of a custom assertion provider could be "public static void IsOfType<T>(this Assert assert, object obj)" - /// Users could then use a syntax similar to the default assertions which in this case is "Assert.That.IsOfType<Dog>(animal);" - /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". + /// For instance, the signature of a custom assertion provider could be public static void IsOfType<T>(this Assert assert, object obj) + /// and the call-site would be Assert.That.IsOfType<Dog>(animal);. + /// For more information, see Create custom assertions with Assert.That. /// public static Assert That { get; } = new(); diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index 30a2558bb4..7673045883 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -21,10 +21,15 @@ private CollectionAssert() /// Gets the singleton instance of the CollectionAssert functionality. /// /// + /// /// Users can use this to plug-in custom assertions through C# extension methods. - /// For instance, the signature of a custom assertion provider could be "public static void AreEqualUnordered(this CollectionAssert customAssert, ICollection expected, ICollection actual)" - /// Users could then use a syntax similar to the default assertions which in this case is "CollectionAssert.That.AreEqualUnordered(list1, list2);" - /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". + /// For instance, the signature of a custom assertion provider could be public static void AreEqualUnordered(this CollectionAssert customAssert, ICollection expected, ICollection actual) + /// and the call-site would be CollectionAssert.That.AreEqualUnordered(list1, list2);. + /// + /// + /// For new custom assertions, prefer extending instead, because is likely to be deprecated in a future release. + /// For more information, see Extension hooks on StringAssert and CollectionAssert. + /// /// public static CollectionAssert That { get; } = new(); diff --git a/src/TestFramework/TestFramework/Assertions/StringAssert.cs b/src/TestFramework/TestFramework/Assertions/StringAssert.cs index 08106d7e22..9409fe5a44 100644 --- a/src/TestFramework/TestFramework/Assertions/StringAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/StringAssert.cs @@ -19,10 +19,15 @@ private StringAssert() /// Gets the singleton instance of the StringAssert functionality. /// /// + /// /// Users can use this to plug-in custom assertions through C# extension methods. - /// For instance, the signature of a custom assertion provider could be "public static void ContainsWords(this StringAssert customAssert, string value, ICollection substrings)" - /// Users could then use a syntax similar to the default assertions which in this case is "StringAssert.That.ContainsWords(value, substrings);" - /// More documentation is at "https://github.com/Microsoft/testfx/docs/README.md". + /// For instance, the signature of a custom assertion provider could be public static void ContainsWords(this StringAssert customAssert, string value, ICollection substrings) + /// and the call-site would be StringAssert.That.ContainsWords(value, substrings);. + /// + /// + /// For new custom assertions, prefer extending instead, because is likely to be deprecated in a future release. + /// For more information, see Extension hooks on StringAssert and CollectionAssert. + /// /// public static StringAssert That { get; } = new(); From 78eeb017331fb62e88144aee8c5b5d4d8022e0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2026 15:54:18 +0200 Subject: [PATCH 2/2] Add inline blocks for That property custom assertions Issue #8818 reported two problems: the broken link AND limited in-place info on how to use the property. The previous commit fixed the link; this commit addresses the second part by adding copy-pasteable code blocks to each That property: - Assert.That -> IsPrime extension method - StringAssert.That -> ContainsWords extension method - CollectionAssert.That -> AreEqualUnordered extension method These examples surface directly in IntelliSense, so developers can see complete usage without leaving their IDE. Closes #8818 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TestFramework/Assertions/Assert.cs | 31 ++++++++++++++++++ .../Assertions/CollectionAssert.cs | 30 +++++++++++++++++ .../TestFramework/Assertions/StringAssert.cs | 32 +++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index aa16dfab46..6fa6f41ca7 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -24,6 +24,37 @@ private Assert() /// and the call-site would be Assert.That.IsOfType<Dog>(animal);. /// For more information, see Create custom assertions with Assert.That. /// + /// + /// The following example defines a custom IsPrime assertion as an extension method on + /// and invokes it through Assert.That: + /// + /// using System; + /// using System.Linq; + /// using Microsoft.VisualStudio.TestTools.UnitTesting; + /// + /// public static class CustomAssertExtensions + /// { + /// public static void IsPrime(this Assert assert, int value) + /// { + /// if (value < 2 || Enumerable.Range(2, (int)Math.Sqrt(value) - 1).Any(i => value % i == 0)) + /// { + /// throw new AssertFailedException($"Assert.That.IsPrime failed. Value <{value}> is not a prime number."); + /// } + /// } + /// } + /// + /// [TestClass] + /// public class CalculatorTests + /// { + /// [TestMethod] + /// public void NextPrime_ReturnsPrime() + /// { + /// int result = new Calculator().NextPrime(10); + /// Assert.That.IsPrime(result); + /// } + /// } + /// + /// public static Assert That { get; } = new(); /// diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index 7673045883..2393ac17d0 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -31,6 +31,36 @@ private CollectionAssert() /// For more information, see Extension hooks on StringAssert and CollectionAssert. /// /// + /// + /// The following example defines a custom AreEqualUnordered assertion as an extension method + /// on and invokes it through CollectionAssert.That: + /// + /// using System.Collections.Generic; + /// using System.Linq; + /// using Microsoft.VisualStudio.TestTools.UnitTesting; + /// + /// public static class CustomCollectionAssertExtensions + /// { + /// public static void AreEqualUnordered<T>(this CollectionAssert collectionAssert, IEnumerable<T> expected, IEnumerable<T> actual) + /// { + /// if (!expected.OrderBy(x => x).SequenceEqual(actual.OrderBy(x => x))) + /// { + /// throw new AssertFailedException("CollectionAssert.That.AreEqualUnordered failed. Collections do not contain the same elements."); + /// } + /// } + /// } + /// + /// [TestClass] + /// public class SetTests + /// { + /// [TestMethod] + /// public void Items_MatchRegardlessOfOrder() + /// { + /// CollectionAssert.That.AreEqualUnordered(new[] { 1, 2, 3 }, new[] { 3, 1, 2 }); + /// } + /// } + /// + /// public static CollectionAssert That { get; } = new(); #endregion diff --git a/src/TestFramework/TestFramework/Assertions/StringAssert.cs b/src/TestFramework/TestFramework/Assertions/StringAssert.cs index 9409fe5a44..f7ab02bc93 100644 --- a/src/TestFramework/TestFramework/Assertions/StringAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/StringAssert.cs @@ -29,6 +29,38 @@ private StringAssert() /// For more information, see Extension hooks on StringAssert and CollectionAssert. /// /// + /// + /// The following example defines a custom ContainsWords assertion as an extension method + /// on and invokes it through StringAssert.That: + /// + /// using System.Collections.Generic; + /// using Microsoft.VisualStudio.TestTools.UnitTesting; + /// + /// public static class CustomStringAssertExtensions + /// { + /// public static void ContainsWords(this StringAssert stringAssert, string value, IEnumerable<string> words) + /// { + /// foreach (string word in words) + /// { + /// if (value == null || !value.Contains(word)) + /// { + /// throw new AssertFailedException($"StringAssert.That.ContainsWords failed. Word <{word}> not found in <{value}>."); + /// } + /// } + /// } + /// } + /// + /// [TestClass] + /// public class MessageTests + /// { + /// [TestMethod] + /// public void Greeting_ContainsExpectedWords() + /// { + /// StringAssert.That.ContainsWords("Hello, world!", new[] { "Hello", "world" }); + /// } + /// } + /// + /// public static StringAssert That { get; } = new(); #endregion