From da90125e55cc61a03e043072f4b1139d3483d866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 9 Dec 2025 21:56:34 +0100 Subject: [PATCH 1/4] wip --- samples/Playground/Tests.cs | 3 +- .../ObjectModel/TestFailedException.cs | 9 ++ .../Assertions/Assert.AreEqual.cs | 16 ++- .../Execution/TestMethodInfoTests.cs | 17 +++ .../Assertions/AssertTests.AreEqualTests.cs | 100 ++++++++++++++++++ 5 files changed, 142 insertions(+), 3 deletions(-) diff --git a/samples/Playground/Tests.cs b/samples/Playground/Tests.cs index b77a79c78b..c5e6d7ccb0 100644 --- a/samples/Playground/Tests.cs +++ b/samples/Playground/Tests.cs @@ -14,6 +14,5 @@ public class TestClass { [TestMethod] public void Test1() - { - } + => Assert.AreEqual("a", "b", ignoreCase: true); } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs index 965723b4c9..e9f272c3e1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestFailedException.cs @@ -35,6 +35,15 @@ public TestFailedException(UTF.UnitTestOutcome outcome, string errorMessage, Sta Outcome = outcome; StackTraceInformation = stackTraceInformation; + + // Copy Data from the real exception to preserve assertion metadata + if (realException?.Data is not null) + { + foreach (object key in realException.Data.Keys) + { + Data[key] = realException.Data[key]; + } + } } /// diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs index 247607a66b..e412584694 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs @@ -700,7 +700,21 @@ private static void ThrowAssertAreEqualFailed(string? expected, string? actual, finalMessage = FormatStringComparisonMessage(expected, actual, userMessage); } - ThrowAssertFailed("Assert.AreEqual", finalMessage); + AssertFailedException exception = new( + string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, "Assert.AreEqual", finalMessage)); + + // Store expected and actual values in exception Data for better tooling support + if (expected is not null) + { + exception.Data["assert.expected"] = expected; + } + + if (actual is not null) + { + exception.Data["assert.actual"] = actual; + } + + throw exception; } /// diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs index 3ef3f09210..0b5aaaee15 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodInfoTests.cs @@ -462,6 +462,23 @@ public async Task TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestCon " at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests.<>c.b__", StringComparison.Ordinal).Should().BeTrue(); } + public async Task TestMethodInfoInvokeShouldPreserveExceptionDataWhenWrappingException() + { + var innerException = new UTF.AssertFailedException("Assert failed"); + innerException.Data["assert.expected"] = "expectedValue"; + innerException.Data["assert.actual"] = "actualValue"; + innerException.Data["custom.key"] = "customValue"; + + DummyTestClass.TestMethodBody = instance => throw innerException; + + var exception = (await _testMethodInfo.InvokeAsync(null)).TestFailureException as TestFailedException; + + exception.Should().NotBeNull(); + exception.Data["assert.expected"].Should().Be("expectedValue"); + exception.Data["assert.actual"].Should().Be("actualValue"); + exception.Data["custom.key"].Should().Be("customValue"); + } + public async Task TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContextAndTestContextProperty_InitializeBothTestContexts() { ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(TestContext)])!; diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index 1ba1daaad9..a05dce3724 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -1722,4 +1722,104 @@ private void StringPreviewsAreEqual(string expected, string actual) """); } } + + public void AreEqualString_ShouldStoreExpectedAndActualInExceptionData() + { + string expected = "expected value"; + string actual = "actual value"; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualString_WithIgnoreCase_ShouldStoreExpectedAndActualInExceptionData() + { + string expected = "Expected Value"; + string actual = "actual value"; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual, ignoreCase: false); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception!.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualString_WithCulture_ShouldStoreExpectedAndActualInExceptionData() + { + string expected = "expected value"; + string actual = "actual value"; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual, ignoreCase: false, CultureInfo.InvariantCulture); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception!.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualString_WithNullExpected_ShouldNotStoreNullInExceptionData() + { + string? expected = null; + string actual = "actual value"; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data.Contains("assert.expected").Should().BeFalse(); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualString_WithNullActual_ShouldNotStoreNullInExceptionData() + { + string expected = "expected value"; + string? actual = null; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data.Contains("assert.actual").Should().BeFalse(); + } } From 54df58f7ca8e072e0560902af9ffef96f00fb6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 10 Dec 2025 13:20:16 +0100 Subject: [PATCH 2/4] Add to TestResult and flow to MTP --- .../EngineConstants.cs | 6 ++ .../Extensions/TestResultExtensions.cs | 10 ++++ .../ObjectModel/ObjectModelConverters.cs | 41 +++++++++++++- .../Attributes/TestMethod/TestResult.cs | 14 +++++ .../Extensions/TestResultExtensionsTests.cs | 55 +++++++++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs b/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs index eca1917fb6..a52885cd02 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs @@ -83,6 +83,10 @@ internal static class EngineConstants internal static readonly TestProperty InnerResultsCountProperty = TestProperty.Register("InnerResultsCount", InnerResultsCountLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestResult)); + internal static readonly TestProperty AssertActualProperty = TestProperty.Register("AssertActual", AssertActualLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestResult)); + + internal static readonly TestProperty AssertExpectedProperty = TestProperty.Register("AssertExpected", AssertExpectedLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestResult)); + internal static readonly TestProperty TestRunIdProperty = TestProperty.Register(TestRunId, TestRunId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase)); internal static readonly TestProperty TestPlanIdProperty = TestProperty.Register(TestPlanId, TestPlanId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase)); @@ -144,6 +148,8 @@ internal static class EngineConstants private const string ExecutionIdLabel = "ExecutionId"; private const string ParentExecIdLabel = "ParentExecId"; private const string InnerResultsCountLabel = "InnerResultsCount"; + private const string AssertActualLabel = "AssertActual"; + private const string AssertExpectedLabel = "AssertExpected"; private const string WorkItemIdsLabel = "WorkItemIds"; private const string TestRunId = "__Tfs_TestRunId__"; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs b/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs index 01afe0677e..1694c6a045 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs @@ -51,6 +51,16 @@ internal static VSTestTestResult ToTestResult(this TestResult frameworkTestResul testResult.SetPropertyValue(EngineConstants.ParentExecIdProperty, frameworkTestResult.ParentExecId); testResult.SetPropertyValue(EngineConstants.InnerResultsCountProperty, frameworkTestResult.InnerResultsCount); + if (frameworkTestResult.ExceptionAssertActual is not null) + { + testResult.SetPropertyValue(EngineConstants.AssertActualProperty, frameworkTestResult.ExceptionAssertActual); + } + + if (frameworkTestResult.ExceptionAssertExpected is not null) + { + testResult.SetPropertyValue(EngineConstants.AssertExpectedProperty, frameworkTestResult.ExceptionAssertExpected); + } + if (!StringEx.IsNullOrEmpty(frameworkTestResult.LogOutput)) { VSTestTestResultMessage message = new(VSTestTestResultMessage.StandardOutCategory, frameworkTestResult.LogOutput); diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs index 850d7edce7..51e15477c4 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -34,6 +34,18 @@ internal static class ObjectModelConverters valueType: typeof(string), owner: typeof(TestCase)); + private static readonly TestProperty AssertActualProperty = TestProperty.Register( + id: "AssertActual", + label: "AssertActual", + valueType: typeof(string), + owner: typeof(TestResult)); + + private static readonly TestProperty AssertExpectedProperty = TestProperty.Register( + id: "AssertExpected", + label: "AssertExpected", + valueType: typeof(string), + owner: typeof(TestResult)); + private static readonly Uri ExecutorUri = new(Constants.ExecutorUri); /// @@ -229,11 +241,21 @@ private static void AddOutcome(this TestNode testNode, TestResult testResult) break; case TestOutcome.NotFound: - testNode.Properties.Add(new ErrorTestNodeStateProperty(new VSTestException(testResult.ErrorMessage ?? "Not found", testResult.ErrorStackTrace))); + { + VSTestException exception = new(testResult.ErrorMessage ?? "Not found", testResult.ErrorStackTrace); + AddAssertDataToException(exception, testResult); + testNode.Properties.Add(new ErrorTestNodeStateProperty(exception)); + } + break; case TestOutcome.Failed: - testNode.Properties.Add(new FailedTestNodeStateProperty(new VSTestException(testResult.ErrorMessage, testResult.ErrorStackTrace))); + { + VSTestException exception = new(testResult.ErrorMessage, testResult.ErrorStackTrace); + AddAssertDataToException(exception, testResult); + testNode.Properties.Add(new FailedTestNodeStateProperty(exception)); + } + break; // It seems that NUnit inconclusive tests are reported as None which should be considered as Skipped. @@ -249,6 +271,21 @@ private static void AddOutcome(this TestNode testNode, TestResult testResult) } } + private static void AddAssertDataToException(VSTestException exception, TestResult testResult) + { + string? assertActual = testResult.GetPropertyValue(AssertActualProperty, defaultValue: null); + if (assertActual is not null) + { + exception.Data["assert.actual"] = assertActual; + } + + string? assertExpected = testResult.GetPropertyValue(AssertExpectedProperty, defaultValue: null); + if (assertExpected is not null) + { + exception.Data["assert.expected"] = assertExpected; + } + } + internal static void FixUpTestCase(this TestCase testCase, string? testAssemblyPath = null) { // To help framework authors using code generator, we replace the Source property of the test case with the diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs index 6dd3a731ed..ea7f997fff 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs @@ -70,6 +70,16 @@ public Exception? TestFailureException ExceptionMessage = field.Message; ExceptionStackTrace = field.StackTrace; + + if (field.Data.Contains("assert.actual")) + { + ExceptionAssertActual = field.Data["assert.actual"]?.ToString(); + } + + if (field.Data.Contains("assert.expected")) + { + ExceptionAssertExpected = field.Data["assert.expected"]?.ToString(); + } } } @@ -77,6 +87,10 @@ public Exception? TestFailureException internal string? ExceptionStackTrace { get; set; } + internal string? ExceptionAssertActual { get; set; } + + internal string? ExceptionAssertExpected { get; set; } + /// /// Gets or sets the output of the message logged by test code. /// diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs index 9d6a00670f..f730285f33 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs @@ -184,4 +184,59 @@ public void ToUnitTestResultsShouldHaveResultsFileProvidedToTestResult() var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); convertedResult.Attachments[0].Attachments[0].Description.Should().Be(resultFile); } + + public void ToUnitTestResultsShouldSetAssertActualWhenProvided() + { + var exception = new Exception("Test failed"); + exception.Data["assert.actual"] = "actualValue"; + + var result = new TestResult { TestFailureException = exception }; + var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); + + ((string)convertedResult.GetPropertyValue(EngineConstants.AssertActualProperty)!).Should().Be("actualValue"); + } + + public void ToUnitTestResultsShouldSetAssertExpectedWhenProvided() + { + var exception = new Exception("Test failed"); + exception.Data["assert.expected"] = "expectedValue"; + + var result = new TestResult { TestFailureException = exception }; + var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); + + ((string)convertedResult.GetPropertyValue(EngineConstants.AssertExpectedProperty)!).Should().Be("expectedValue"); + } + + public void ToUnitTestResultsShouldSetBothAssertActualAndExpectedWhenProvided() + { + var exception = new Exception("Test failed"); + exception.Data["assert.actual"] = "actualValue"; + exception.Data["assert.expected"] = "expectedValue"; + + var result = new TestResult { TestFailureException = exception }; + var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); + + ((string)convertedResult.GetPropertyValue(EngineConstants.AssertActualProperty)!).Should().Be("actualValue"); + ((string)convertedResult.GetPropertyValue(EngineConstants.AssertExpectedProperty)!).Should().Be("expectedValue"); + } + + public void ToUnitTestResultsShouldNotSetAssertActualWhenNotProvided() + { + var exception = new Exception("Test failed"); + + var result = new TestResult { TestFailureException = exception }; + var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); + + convertedResult.GetPropertyValue(EngineConstants.AssertActualProperty).Should().BeNull(); + } + + public void ToUnitTestResultsShouldNotSetAssertExpectedWhenNotProvided() + { + var exception = new Exception("Test failed"); + + var result = new TestResult { TestFailureException = exception }; + var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new()); + + convertedResult.GetPropertyValue(EngineConstants.AssertExpectedProperty).Should().BeNull(); + } } From 6bbda78632e1cab9efd39ced771cc23e40ad0b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 10 Dec 2025 14:21:23 +0100 Subject: [PATCH 3/4] Update handling --- samples/Playground/Tests.cs | 7 +- .../Assertions/Assert.AreEqual.cs | 20 +-- .../TestFramework/Assertions/Assert.cs | 64 +++++++++ .../Assertions/AssertTests.AreEqualTests.cs | 129 ++++++++++++++++++ 4 files changed, 202 insertions(+), 18 deletions(-) diff --git a/samples/Playground/Tests.cs b/samples/Playground/Tests.cs index c5e6d7ccb0..a7d7553fac 100644 --- a/samples/Playground/Tests.cs +++ b/samples/Playground/Tests.cs @@ -14,5 +14,10 @@ public class TestClass { [TestMethod] public void Test1() - => Assert.AreEqual("a", "b", ignoreCase: true); + { + string commonPart = new('a', 1000); + string expected = commonPart + "expected"; + string actual = commonPart + "actual"; + Assert.AreEqual(expected, actual, ignoreCase: true); + } } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs index e412584694..009dd665c1 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs @@ -643,7 +643,7 @@ private static string FormatStringDifferenceMessage(string expected, string actu } [DoesNotReturn] - private static void ThrowAssertAreEqualFailed(object? expected, object? actual, string userMessage) + private static void ThrowAssertAreEqualFailed(T? expected, T? actual, string userMessage) { string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType()) ? string.Format( @@ -662,7 +662,7 @@ private static void ThrowAssertAreEqualFailed(object? expected, object? actual, userMessage, ReplaceNulls(expected), ReplaceNulls(actual)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertFailed("Assert.AreEqual", finalMessage, expected, actual); } [DoesNotReturn] @@ -700,21 +700,7 @@ private static void ThrowAssertAreEqualFailed(string? expected, string? actual, finalMessage = FormatStringComparisonMessage(expected, actual, userMessage); } - AssertFailedException exception = new( - string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, "Assert.AreEqual", finalMessage)); - - // Store expected and actual values in exception Data for better tooling support - if (expected is not null) - { - exception.Data["assert.expected"] = expected; - } - - if (actual is not null) - { - exception.Data["assert.actual"] = actual; - } - - throw exception; + ThrowAssertFailed("Assert.AreEqual", finalMessage, expected, actual); } /// diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index 2035fbf618..a7899d4693 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -40,6 +40,70 @@ internal static void ThrowAssertFailed(string assertionName, string? message) => throw new AssertFailedException( string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, assertionName, message)); + /// + /// Helper function that creates and throws an AssertionFailedException with expected and actual values. + /// + /// + /// The type of the expected and actual values. + /// + /// + /// name of the assertion throwing an exception. + /// + /// + /// The assertion failure message. + /// + /// + /// Expected value to store in exception data. + /// + /// + /// Actual value to store in exception data. + /// + [DoesNotReturn] + [StackTraceHidden] + internal static void ThrowAssertFailed(string assertionName, string? message, T? expected, T? actual) + { + AssertFailedException exception = new( + string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AssertionFailed, assertionName, message)); + + // Store expected and actual values in exception Data for types with known good ToString implementations + if (HasKnownGoodToString(expected)) + { + exception.Data["assert.expected"] = expected; + } + + if (HasKnownGoodToString(actual)) + { + exception.Data["assert.actual"] = actual; + } + + throw exception; + } + + private static bool HasKnownGoodToString([NotNullWhen(true)] object? value) + { + if (value is null) + { + return false; + } + + Type type = value.GetType(); + + // Primitive types and string + if (type.IsPrimitive || type == typeof(string)) + { + return true; + } + + // Common types with good ToString implementations + return type == typeof(decimal) + || type == typeof(DateTime) + || type == typeof(DateTimeOffset) + || type == typeof(TimeSpan) + || type == typeof(Guid) + || type == typeof(Uri) + || type.IsEnum; + } + /// /// Builds the formatted message using the given user format message and parameters. /// diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index a05dce3724..86d6cba4bf 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -1822,4 +1822,133 @@ public void AreEqualString_WithNullActual_ShouldNotStoreNullInExceptionData() exception!.Data["assert.expected"].Should().Be(expected); exception.Data.Contains("assert.actual").Should().BeFalse(); } + + public void AreEqualInt_ShouldStoreExpectedAndActualInExceptionData() + { + int expected = 42; + int actual = 99; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualDecimal_ShouldStoreExpectedAndActualInExceptionData() + { + decimal expected = 123.45m; + decimal actual = 678.90m; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualDateTime_ShouldStoreExpectedAndActualInExceptionData() + { + DateTime expected = new(2023, 1, 1, 10, 30, 0); + DateTime actual = new(2024, 12, 31, 15, 45, 0); + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualGuid_ShouldStoreExpectedAndActualInExceptionData() + { + var expected = Guid.NewGuid(); + var actual = Guid.NewGuid(); + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualEnum_ShouldStoreExpectedAndActualInExceptionData() + { + DayOfWeek expected = DayOfWeek.Monday; + DayOfWeek actual = DayOfWeek.Friday; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data["assert.expected"].Should().Be(expected); + exception.Data["assert.actual"].Should().Be(actual); + } + + public void AreEqualCustomObject_ShouldNotStoreInExceptionData() + { + var expected = new CustomObject { Value = 1 }; + var actual = new CustomObject { Value = 2 }; + + AssertFailedException? exception = null; + try + { + Assert.AreEqual(expected, actual); + } + catch (AssertFailedException ex) + { + exception = ex; + } + + exception.Should().NotBeNull(); + exception!.Data.Contains("assert.expected").Should().BeFalse(); + exception.Data.Contains("assert.actual").Should().BeFalse(); + } + + private class CustomObject + { + public int Value { get; set; } + + public override bool Equals(object? obj) => obj is CustomObject other && Value == other.Value; + + public override int GetHashCode() => Value.GetHashCode(); + } } From de1a50898fcf92514299672d0e52d1bc4ab76464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 10 Dec 2025 16:08:43 +0100 Subject: [PATCH 4/4] Apply suggestion from @Evangelink --- samples/Playground/Tests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/Playground/Tests.cs b/samples/Playground/Tests.cs index a7d7553fac..8fac9fdea2 100644 --- a/samples/Playground/Tests.cs +++ b/samples/Playground/Tests.cs @@ -15,9 +15,9 @@ public class TestClass [TestMethod] public void Test1() { - string commonPart = new('a', 1000); - string expected = commonPart + "expected"; - string actual = commonPart + "actual"; - Assert.AreEqual(expected, actual, ignoreCase: true); + // string commonPart = new('a', 1000); + // string expected = commonPart + "expected"; + // string actual = commonPart + "actual"; + // Assert.AreEqual(expected, actual, ignoreCase: true); } }