-
Notifications
You must be signed in to change notification settings - Fork 292
Add Assert.Scope() for soft assertions
#7355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| namespace Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
|
||
| /// <summary> | ||
| /// A collection of helper classes to test various conditions within | ||
| /// unit tests. If the condition being tested is not met, an exception | ||
| /// is thrown. | ||
| /// </summary> | ||
| public sealed partial class Assert | ||
| { | ||
| /// <summary> | ||
| /// Creates a new assertion scope that collects assertion failures instead of throwing them immediately. | ||
| /// When the returned scope is disposed, all collected failures are thrown as a single <see cref="AssertFailedException"/>. | ||
| /// </summary> | ||
| /// <returns>An <see cref="IDisposable"/> representing the assertion scope.</returns> | ||
| /// <example> | ||
| /// <code> | ||
| /// using (Assert.Scope()) | ||
| /// { | ||
| /// Assert.AreEqual(1, 2); // collected, not thrown | ||
| /// Assert.IsTrue(false); // collected, not thrown | ||
| /// } | ||
| /// // AssertFailedException is thrown here with all collected failures. | ||
| /// </code> | ||
| /// </example> | ||
| public static IDisposable Scope() => new AssertScope(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,8 +37,19 @@ private Assert() | |
| [DoesNotReturn] | ||
| [StackTraceHidden] | ||
| internal static void ThrowAssertFailed(string assertionName, string? message) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given this PR and #2033, I think it'd be good to expose a "ReportFailure" method that could be used by devs extending MSTest assertions so they would benefits from the various features (launch debugger, soft assertions....). I am not doing it here because we can decide not to go with it for now. |
||
| => throw new AssertFailedException( | ||
| string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, assertionName, message)); | ||
| { | ||
| var assertionFailedException = new AssertFailedException(string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, assertionName, message)); | ||
| AssertScope? scope = AssertScope.Current; | ||
| if (scope is not null) | ||
| { | ||
| scope.AddError(assertionFailedException); | ||
| #pragma warning disable CS8763 // A method marked [DoesNotReturn] should not return. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Youssef1313 I don't know what we prefer but it feels better for me to say we continue to help compiler with the default behavior and we know we may have some FPs when code is used under assertion scope. It would otherwise be a breaking change for many people if we had to update all assertion APIs to no longer respect some of these DoesNotReturn compilation indication. |
||
| return; | ||
| #pragma warning restore CS8763 // A method marked [DoesNotReturn] should not return. | ||
| } | ||
|
|
||
| throw assertionFailedException; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Builds the formatted message using the given user format message and parameters. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| namespace Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
|
||
| /// <summary> | ||
| /// Represents a scope in which assertion failures are collected instead of thrown immediately. | ||
| /// When the scope is disposed, all collected failures are thrown as a single <see cref="AssertFailedException"/>. | ||
| /// </summary> | ||
| internal sealed class AssertScope : IDisposable | ||
| { | ||
| private static readonly AsyncLocal<AssertScope?> CurrentScope = new(); | ||
|
|
||
| private readonly List<AssertFailedException> _errors = []; | ||
| private readonly AssertScope? _previousScope; | ||
| private bool _disposed; | ||
|
|
||
| internal AssertScope() | ||
| { | ||
| _previousScope = CurrentScope.Value; | ||
| CurrentScope.Value = this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the current active <see cref="AssertScope"/>, or <see langword="null"/> if no scope is active. | ||
| /// </summary> | ||
| internal static AssertScope? Current => CurrentScope.Value; | ||
|
|
||
| /// <summary> | ||
| /// Adds an assertion failure message to the current scope. | ||
| /// </summary> | ||
| /// <param name="error">The assertion failure message.</param> | ||
| internal void AddError(AssertFailedException error) => _errors.Add(error); | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Dispose() | ||
| { | ||
| if (_disposed) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| _disposed = true; | ||
| CurrentScope.Value = _previousScope; | ||
|
|
||
| if (_errors.Count > 0) | ||
| { | ||
| throw new AssertFailedException( | ||
| string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertScopeFailure, _errors.Count), | ||
| new AggregateException(_errors)); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| #nullable enable | ||
| static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Scope() -> System.IDisposable! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public API is quite limited, do we want to make it experimental still?