// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using AwesomeAssertions; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests { // Shared expected message for the structured failure produced by `Assert.AreEqual(1, 2)`. // Used across the scope soft-failure tests below to reduce churn when the structured-message // format evolves; update this single constant rather than every assertion site. private const string AreEqual1And2StructuredMessage = """ Assertion failed. Expected values to be equal. expected: 1 actual: 2 Assert.AreEqual(1, 2) """; public void Scope_NoFailures_DoesNotThrow() { Action action = () => { using (Assert.Scope()) { Assert.IsTrue(true); Assert.AreEqual(1, 1); } }; action.Should().NotThrow(); } public void Scope_SingleFailure_ThrowsOnDispose() { IDisposable scope = Assert.Scope(); Assert.AreEqual(1, 2); Action action = () => scope.Dispose(); action.Should().Throw() .WithMessage(AreEqual1And2StructuredMessage); } public void Scope_MultipleFailures_CollectsAllErrors() { Action action = () => { using (Assert.Scope()) { Assert.AreEqual(1, 2); Assert.IsTrue(false); } }; AggregateException innerException = action.Should().Throw() .WithMessage("2 assertion(s) failed within the assert scope.") .WithInnerException() .Which; innerException.InnerExceptions.Should().HaveCount(2); innerException.InnerExceptions[0].Message.Should().Be(AreEqual1And2StructuredMessage); innerException.InnerExceptions[1].Message.Should().Be( """ Assertion failed. Expected condition to be true. actual: false Assert.IsTrue(false) """); } public void Scope_AfterDispose_AssertionsThrowNormally() { // Completing a scope should restore normal behavior. try { using (Assert.Scope()) { // intentionally empty scope } } catch { // ignore } Action action = () => Assert.IsTrue(false); action.Should().Throw(); } public void Scope_NestedScope_ThrowsInvalidOperationException() { Action action = () => { using (Assert.Scope()) { using (Assert.Scope()) { // Should never reach here } } }; action.Should().Throw() .WithMessage("Nested assert scopes are not allowed. Dispose the current scope before creating a new one."); } public void Scope_DoubleDispose_DoesNotThrowTwice() { IDisposable scope = Assert.Scope(); Assert.AreEqual(1, 2); Action firstDispose = () => scope.Dispose(); firstDispose.Should().Throw() .WithMessage(AreEqual1And2StructuredMessage); // Second dispose should be a no-op Action secondDispose = () => scope.Dispose(); secondDispose.Should().NotThrow(); } public void Scope_AssertFail_IsHardFailure() { Action action = () => { using (Assert.Scope()) { Assert.Fail("first failure"); Assert.Fail("second failure"); } }; // Assert.Fail is a hard assertion — it throws immediately, even within a scope. action.Should().Throw() .WithMessage( """ Assertion failed. first failure """); } public void Scope_AssertIsNotNull_IsSoftFailure() { object? value = null; Action action = () => { using (Assert.Scope()) { Assert.IsNotNull(value); Assert.AreEqual(1, 2); } }; // Assert.IsNotNull is a soft assertion — failure is collected within a scope. AggregateException innerException = action.Should().Throw() .WithMessage("2 assertion(s) failed within the assert scope.") .WithInnerException() .Which; innerException.InnerExceptions.Should().HaveCount(2); innerException.InnerExceptions[0].Message.Should().Be( """ Assertion failed. Expected value to not be null. actual: null Assert.IsNotNull(value) """); innerException.InnerExceptions[1].Message.Should().Be(AreEqual1And2StructuredMessage); } public void Scope_AssertIsInstanceOfType_IsSoftFailure() { object value = "hello"; Action action = () => { using (Assert.Scope()) { Assert.IsInstanceOfType(value, typeof(int)); Assert.AreEqual(1, 2); } }; // Assert.IsInstanceOfType is a soft assertion — failure is collected within a scope. AggregateException innerException = action.Should().Throw() .WithMessage("2 assertion(s) failed within the assert scope.") .WithInnerException() .Which; innerException.InnerExceptions.Should().HaveCount(2); innerException.InnerExceptions[0].Message.Should().Be( """ Assertion failed. Expected value to be of type Int32 (or derived). expected type: System.Int32 (or derived) actual type: System.String Assert.IsInstanceOfType(value) """); innerException.InnerExceptions[1].Message.Should().Be(AreEqual1And2StructuredMessage); } public void Scope_AssertIsExactInstanceOfType_IsSoftFailure() { object value = "hello"; Action action = () => { using (Assert.Scope()) { Assert.IsExactInstanceOfType(value, typeof(object)); Assert.AreEqual(1, 2); } }; // Assert.IsExactInstanceOfType is a soft assertion — failure is collected within a scope. AggregateException innerException = action.Should().Throw() .WithMessage("2 assertion(s) failed within the assert scope.") .WithInnerException() .Which; innerException.InnerExceptions.Should().HaveCount(2); innerException.InnerExceptions[0].Message.Should().Be( """ Assertion failed. Expected value to be exactly of type Object. expected type: System.Object actual type: System.String Assert.IsExactInstanceOfType(value) """); innerException.InnerExceptions[1].Message.Should().Be(AreEqual1And2StructuredMessage); } public void Scope_AssertContainsSingle_IsSoftFailure() { int[] items = [1, 2, 3]; Action action = () => { using (Assert.Scope()) { Assert.ContainsSingle(items); Assert.AreEqual(1, 2); } }; // Assert.ContainsSingle is a soft assertion — failure is collected within a scope. AggregateException innerException = action.Should().Throw() .WithMessage("2 assertion(s) failed within the assert scope.") .WithInnerException() .Which; innerException.InnerExceptions.Should().HaveCount(2); innerException.InnerExceptions[0].Message.Should().Be( """ Assertion failed. Expected collection to contain exactly one element. expected count: 1 actual count: 3 Assert.ContainsSingle(items) """); innerException.InnerExceptions[1].Message.Should().Be(AreEqual1And2StructuredMessage); } }