From bd02aea8de5ed9c38c88c5aa37836ca2389fef34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2026 15:04:01 +0200 Subject: [PATCH 1/3] Document Assert.That custom assertions and deprecate StringAssert/CollectionAssert guidance - Add 'Create custom assertions with Assert.That' section explaining the singleton extensibility hook with a worked extension method example. - Note that StringAssert.That and CollectionAssert.That exist for the same pattern but recommend Assert.That for new code. - Disambiguate the singleton property from the MSTest 3.8 Assert.That(() => condition) expression-tree method. - Strengthen messaging that StringAssert and CollectionAssert are likely to be deprecated and aren't recommended due to discoverability issues. - Add ai-usage: ai-assisted and bump ms.date. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...testing-mstest-writing-tests-assertions.md | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md index 16a7bb4cbf08c..4bb05f2a48fc3 100644 --- a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md +++ b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md @@ -3,7 +3,8 @@ title: MSTest assertions description: Learn about MSTest assertions including Assert, StringAssert, and CollectionAssert classes for validating test results. author: Evangelink ms.author: amauryleve -ms.date: 07/15/2025 +ms.date: 06/04/2026 +ai-usage: ai-assisted --- # MSTest assertions @@ -20,8 +21,8 @@ MSTest provides three assertion classes: | `StringAssert` | String-specific assertions for patterns, substrings, and comparisons. | | `CollectionAssert` | Collection assertions for comparing and validating collections. | -> [!TIP] -> When functionality exists in both `Assert` and `StringAssert`/`CollectionAssert`, prefer the `Assert` class. The `Assert` class provides better discoverability and is the recommended choice for new code. `StringAssert` and `CollectionAssert` are maintained for backward compatibility. +> [!IMPORTANT] +> For new code, always use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. They're maintained primarily for backward compatibility, but they're not recommended because splitting assertions across three types hurts discoverability—you can't see all available checks in a single IntelliSense list. All assertion methods accept an optional message parameter that displays when the assertion fails, helping you identify the cause: @@ -107,8 +108,8 @@ public async Task AssertExamples() Use the class to compare and examine strings. -> [!NOTE] -> All `StringAssert` methods have equivalents in the `Assert` class. Prefer the `Assert` methods for better discoverability. The `StringAssert` class is maintained for backward compatibility. +> [!WARNING] +> The `StringAssert` class is likely to be deprecated in a future release. It's maintained for backward compatibility only and isn't recommended for new code. All `StringAssert` methods have equivalents on the `Assert` class, which offers better discoverability. To migrate existing usages, see analyzer [MSTEST0046](mstest-analyzers/mstest0046.md). Available APIs are: @@ -122,8 +123,8 @@ Available APIs are: Use the class to compare collections of objects, or to verify the state of a collection. -> [!NOTE] -> When an equivalent method exists in the `Assert` class (such as `Assert.Contains`, `Assert.DoesNotContain`), prefer using `Assert` for better discoverability. The `CollectionAssert` class is maintained primarily for backward compatibility. +> [!WARNING] +> The `CollectionAssert` class is likely to be deprecated in a future release. It's maintained primarily for backward compatibility and isn't recommended for new code. When an equivalent method exists on `Assert` (such as `Assert.Contains`, `Assert.DoesNotContain`, or `Assert.HasCount`), use `Assert` for better discoverability. Available APIs are: @@ -139,6 +140,51 @@ Available APIs are: - - +## Create custom assertions with `Assert.That` + +The built-in assertion methods don't cover every scenario. To extend the assertion infrastructure with your own checks, MSTest exposes the singleton property as an extensibility hook. You add custom assertions as C# extension methods on the `Assert` instance type, and callers invoke them with the familiar `Assert.That.MyAssertion(...)` syntax. + +For better discoverability, organize project-wide assertions in a dedicated static class. Custom assertions reached through `Assert.That` show up alongside the built-in methods in IntelliSense, so consumers don't have to remember a separate helper type. + +### Author a custom assertion + +Add an extension method that targets the `Assert` type and throws when the condition fails: + +```csharp +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."); + } + } +} +``` + +### Use a custom assertion + +After you import the namespace that contains your extension methods, call your custom assertion through `Assert.That`: + +```csharp +[TestMethod] +public void Compute_ReturnsPrime() +{ + int result = _calculator.NextPrime(10); + Assert.That.IsPrime(result); +} +``` + +### Extension hooks on `StringAssert` and `CollectionAssert` + +The and properties expose the same singleton pattern for backward compatibility. For new custom assertions, always target `Assert.That`. Otherwise, your helpers inherit the same discoverability problems as the legacy classes, and they'll need migration if `StringAssert` and `CollectionAssert` are deprecated. + +> [!NOTE] +> Don't confuse the `Assert.That` singleton *property*—used as an extensibility hook—with the `Assert.That(() => condition)` extension *method* added in MSTest 3.8. The latter accepts a boolean expression and produces detailed failure messages by analyzing the expression tree (for example, `Assert.That(() => order.Total > 0)`). The two APIs share a name but serve different purposes. + ## Best practices 1. **Use specific assertions**: Prefer `AreEqual` over `IsTrue(a == b)` for better failure messages. @@ -149,7 +195,9 @@ Available APIs are: 1. **Use `Throws`/`ThrowsExactly` for exceptions**: In MSTest v3.8+, prefer `Assert.Throws`, `Assert.ThrowsExactly`, and their async counterparts (`ThrowsAsync`, `ThrowsExactlyAsync`) over the `ExpectedException` attribute. -1. **Prefer `Assert` over `StringAssert`/`CollectionAssert`**: When functionality exists in both classes, use the `Assert` class for better discoverability and consistency. +1. **Prefer `Assert` over `StringAssert`/`CollectionAssert`**: For better discoverability and consistency, use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. + +1. **Extend `Assert.That` for custom assertions**: For consistent discoverability, add custom assertions as extension methods on `Assert` and invoke them through `Assert.That`. Don't target `StringAssert.That` or `CollectionAssert.That` in new code. ## Related analyzers From 7ef869c778ec0c99c4077cb4fac3b6acb85f8a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2026 15:41:55 +0200 Subject: [PATCH 2/3] Address review: fix IsPrime imports, clarify Assert.That wording, move disambiguation NOTE Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../testing/unit-testing-mstest-writing-tests-assertions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md index 4bb05f2a48fc3..da3b5455a24ee 100644 --- a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md +++ b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md @@ -151,6 +151,8 @@ For better discoverability, organize project-wide assertions in a dedicated stat Add an extension method that targets the `Assert` type and throws when the condition fails: ```csharp +using System; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; public static class CustomAssertExtensions @@ -182,8 +184,10 @@ public void Compute_ReturnsPrime() The and properties expose the same singleton pattern for backward compatibility. For new custom assertions, always target `Assert.That`. Otherwise, your helpers inherit the same discoverability problems as the legacy classes, and they'll need migration if `StringAssert` and `CollectionAssert` are deprecated. +### `Assert.That` property versus `Assert.That(...)` method + > [!NOTE] -> Don't confuse the `Assert.That` singleton *property*—used as an extensibility hook—with the `Assert.That(() => condition)` extension *method* added in MSTest 3.8. The latter accepts a boolean expression and produces detailed failure messages by analyzing the expression tree (for example, `Assert.That(() => order.Total > 0)`). The two APIs share a name but serve different purposes. +> Don't confuse the `Assert.That` singleton *property*—used as an extensibility hook—with the `Assert.That(() => condition)` *method* added in MSTest 3.8. The latter accepts a boolean expression and produces detailed failure messages by analyzing the expression tree (for example, `Assert.That(() => order.Total > 0)`). The two APIs share a name but serve different purposes. ## Best practices From da34c0bced5b0e5590e5f9ecbce4202cb2a21b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sun, 7 Jun 2026 09:36:31 +0200 Subject: [PATCH 3/3] Address MSTest assertions review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...it-testing-mstest-writing-tests-assertions.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md index da3b5455a24ee..8e5a690d88834 100644 --- a/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md +++ b/docs/core/testing/unit-testing-mstest-writing-tests-assertions.md @@ -22,7 +22,7 @@ MSTest provides three assertion classes: | `CollectionAssert` | Collection assertions for comparing and validating collections. | > [!IMPORTANT] -> For new code, always use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. They're maintained primarily for backward compatibility, but they're not recommended because splitting assertions across three types hurts discoverability—you can't see all available checks in a single IntelliSense list. +> For new code, always use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. They're maintained primarily for backward compatibility, but they're not recommended because splitting assertions across three types hurts discoverability. All assertion methods accept an optional message parameter that displays when the assertion fails, helping you identify the cause: @@ -187,21 +187,21 @@ The [!NOTE] -> Don't confuse the `Assert.That` singleton *property*—used as an extensibility hook—with the `Assert.That(() => condition)` *method* added in MSTest 3.8. The latter accepts a boolean expression and produces detailed failure messages by analyzing the expression tree (for example, `Assert.That(() => order.Total > 0)`). The two APIs share a name but serve different purposes. +> Don't confuse the `Assert.That` singleton *property*—used as an extensibility hook—with the `Assert.That(() => condition)` *method* added in MSTest 3.8. The latter accepts a Boolean expression and produces detailed failure messages by analyzing the expression tree (for example, `Assert.That(() => order.Total > 0)`). The two APIs share a name but serve different purposes. ## Best practices -1. **Use specific assertions**: Prefer `AreEqual` over `IsTrue(a == b)` for better failure messages. +- **Use specific assertions**: Prefer `AreEqual` over `IsTrue(a == b)` for better failure messages. -1. **Include descriptive messages**: Help identify failures quickly with clear assertion messages. +- **Include descriptive messages**: Help identify failures quickly with clear assertion messages. -1. **Test one thing at a time**: Each test method should verify a single behavior. +- **Test one thing at a time**: Each test method should verify a single behavior. -1. **Use `Throws`/`ThrowsExactly` for exceptions**: In MSTest v3.8+, prefer `Assert.Throws`, `Assert.ThrowsExactly`, and their async counterparts (`ThrowsAsync`, `ThrowsExactlyAsync`) over the `ExpectedException` attribute. +- **Use `Throws`/`ThrowsExactly` for exceptions**: In MSTest v3.8+, prefer `Assert.Throws`, `Assert.ThrowsExactly`, and their async counterparts (`ThrowsAsync`, `ThrowsExactlyAsync`) over the `ExpectedException` attribute. -1. **Prefer `Assert` over `StringAssert`/`CollectionAssert`**: For better discoverability and consistency, use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. +- **Prefer `Assert` over `StringAssert`/`CollectionAssert`**: For better discoverability and consistency, use the `Assert` class. The `StringAssert` and `CollectionAssert` classes are likely to be deprecated in a future release. -1. **Extend `Assert.That` for custom assertions**: For consistent discoverability, add custom assertions as extension methods on `Assert` and invoke them through `Assert.That`. Don't target `StringAssert.That` or `CollectionAssert.That` in new code. +- **Extend `Assert.That` for custom assertions**: For consistent discoverability, add custom assertions as extension methods on `Assert` and invoke them through `Assert.That`. Don't target `StringAssert.That` or `CollectionAssert.That` in new code. ## Related analyzers