-
Notifications
You must be signed in to change notification settings - Fork 291
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 4 commits
19a1f23
312a4a4
d860b27
b0208b3
6133a25
63dceb7
12d69af
f9133c3
61f218e
888a8a6
c68b24e
ceb625b
61899b1
bb40e99
328b1fb
e84eadc
b9d6aff
bc227a8
13ccc3b
c462a15
086a072
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,32 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| using System.Diagnostics.CodeAnalysis; | ||
|
Check failure on line 4 in src/TestFramework/TestFramework/Assertions/Assert.Scope.cs
|
||
|
|
||
| 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> | ||
| [Experimental("MSTESTEXP", UrlFormat = "https://aka.ms/mstest/diagnostics#{0}")] | ||
| public static IDisposable Scope() => new AssertScope(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| using System.Collections.Concurrent; | ||
|
Check failure on line 4 in src/TestFramework/TestFramework/Assertions/AssertScope.cs
|
||
|
|
||
| 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 ConcurrentQueue<AssertFailedException> _errors = new(); | ||
| private bool _disposed; | ||
|
|
||
| internal AssertScope() | ||
| { | ||
| if (CurrentScope.Value is not null) | ||
| { | ||
| throw new InvalidOperationException(FrameworkMessages.AssertScopeNestedNotAllowed); | ||
| } | ||
|
|
||
| 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) | ||
| { | ||
| ObjectDisposedException.ThrowIf(_disposed, this); | ||
|
Check failure on line 40 in src/TestFramework/TestFramework/Assertions/AssertScope.cs
|
||
| _errors.Enqueue(error); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Dispose() | ||
| { | ||
| if (_disposed) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| _disposed = true; | ||
| CurrentScope.Value = null; | ||
|
|
||
| if (_errors.Count == 1 && _errors.TryDequeue(out AssertFailedException? singleError)) | ||
| { | ||
| throw singleError; | ||
Evangelink marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (_errors.Count > 0) | ||
|
Check failure on line 60 in src/TestFramework/TestFramework/Assertions/AssertScope.cs
|
||
Evangelink marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| 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 | ||
| [MSTESTEXP]static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Scope() -> System.IDisposable! |
Uh oh!
There was an error while loading. Please reload this page.