Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8014b3c
Add assertion formatting helpers and localized resource strings
Evangelink Feb 22, 2026
b2127af
Improve AreEqual/AreNotEqual assertion error messages
Evangelink Feb 22, 2026
9ac4849
Improve AreSame/AreNotSame assertion error messages
Evangelink Feb 22, 2026
375f0b5
Improve IsTrue/IsFalse assertion error messages
Evangelink Feb 22, 2026
bd66356
Improve IsNull/IsNotNull assertion error messages
Evangelink Feb 22, 2026
7e7f07e
Improve IsInstanceOfType/IsNotInstanceOfType assertion error messages
Evangelink Feb 22, 2026
bebe2ef
Improve IsExactInstanceOfType/IsNotExactInstanceOfType assertion erro…
Evangelink Feb 22, 2026
d35c33b
Improve IComparable assertion error messages
Evangelink Feb 22, 2026
23758d2
Improve HasCount/IsEmpty/IsNotEmpty assertion error messages
Evangelink Feb 22, 2026
3705c74
Improve ThrowsException assertion error messages
Evangelink Feb 22, 2026
23cd627
Improve Contains/DoesNotContain/IsInRange assertion error messages
Evangelink Feb 22, 2026
dd88189
Improve StartsWith/DoesNotStartWith assertion error messages
Evangelink Feb 22, 2026
0c10f63
Improve EndsWith/DoesNotEndWith assertion error messages
Evangelink Feb 22, 2026
14a6ec3
Improve MatchesRegex/DoesNotMatchRegex assertion error messages
Evangelink Feb 22, 2026
63e8257
Minor test cleanup for Inconclusive assertion
Evangelink Feb 22, 2026
f559387
Address Copilot feedback
Evangelink Feb 22, 2026
dfe9891
Fix acceptance tests
Evangelink Feb 22, 2026
4422c82
Merge branch 'main' into dev/amauryleve/rework-assert
Evangelink Mar 11, 2026
f725f60
Fix diff caret position
Evangelink Mar 11, 2026
9c96c9b
Use X more chars instead of total length
Evangelink Mar 11, 2026
632c3c1
Use item instead of element
Evangelink Mar 11, 2026
fa263f2
Use more items
Evangelink Mar 11, 2026
2822182
Updates
Evangelink Mar 11, 2026
39b59eb
Simplify user message handling
Evangelink Mar 11, 2026
60d17a9
Update hash text
Evangelink Mar 11, 2026
44c6ca1
Improve
Evangelink Mar 11, 2026
2610e3a
Move expressions to first line
Evangelink Mar 12, 2026
a402e6d
Avoid using wildcard in expected message
Evangelink Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 102 additions & 149 deletions src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs

Large diffs are not rendered by default.

61 changes: 45 additions & 16 deletions src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ internal void ComputeAssertion(string expectedExpression, string actualExpressio
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionTwoParametersMessage, "expected", expectedExpression, "actual", actualExpression) + " ");
ThrowAssertAreSameFailed(_expected, _actual, _builder.ToString());
ThrowAssertAreSameFailed(_expected, _actual, _builder.ToString(), expectedExpression, actualExpression);
}
}

Expand Down Expand Up @@ -80,9 +79,13 @@ internal void ComputeAssertion(string expectedExpression, string actualExpressio
public readonly struct AssertAreNotSameInterpolatedStringHandler<TArgument>
{
private readonly StringBuilder? _builder;
private readonly TArgument? _notExpected;
private readonly TArgument? _actual;

public AssertAreNotSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend)
{
_notExpected = notExpected;
_actual = actual;
shouldAppend = IsAreNotSameFailing(notExpected, actual);
if (shouldAppend)
{
Expand All @@ -94,8 +97,7 @@ internal void ComputeAssertion(string notExpectedExpression, string actualExpres
{
if (_builder is not null)
{
_builder.Insert(0, string.Format(CultureInfo.CurrentCulture, FrameworkMessages.CallerArgumentExpressionTwoParametersMessage, "notExpected", notExpectedExpression, "actual", actualExpression) + " ");
ThrowAssertAreNotSameFailed(_builder.ToString());
ThrowAssertAreNotSameFailed(_notExpected, _actual, _builder.ToString(), notExpectedExpression, actualExpression);
}
}

Expand Down Expand Up @@ -177,26 +179,45 @@ public static void AreSame<T>(T? expected, T? actual, string? message = "", [Cal
return;
}

string userMessage = BuildUserMessageForExpectedExpressionAndActualExpression(message, expectedExpression, actualExpression);
ThrowAssertAreSameFailed(expected, actual, userMessage);
ThrowAssertAreSameFailed(expected, actual, message, expectedExpression, actualExpression);
}

private static bool IsAreSameFailing<T>(T? expected, T? actual)
=> !object.ReferenceEquals(expected, actual);

[DoesNotReturn]
private static void ThrowAssertAreSameFailed<T>(T? expected, T? actual, string userMessage)
private static void ThrowAssertAreSameFailed<T>(T? expected, T? actual, string? userMessage, string expectedExpression, string actualExpression)
{
string finalMessage = userMessage;
string callSite = FormatCallSite("Assert.AreSame", (nameof(expected), expectedExpression), (nameof(actual), actualExpression));
string message = string.IsNullOrEmpty(userMessage) ? string.Empty : userMessage!;

// When both values have the same string representation, include hash codes
// to help the user understand they are different object instances.
string expectedFormatted = FormatValue(expected);
string actualFormatted = FormatValue(actual);
bool sameToString = expected is not null && actual is not null && expectedFormatted == actualFormatted;
string expectedValue = sameToString
? expectedFormatted + $" (Hash={RuntimeHelpers.GetHashCode(expected!)})"
: expectedFormatted;
string actualValue = sameToString
? actualFormatted + $" (Hash={RuntimeHelpers.GetHashCode(actual!)})"
: actualFormatted;

// If value types, add diagnostic hint before parameter details
if (expected is ValueType && actual is ValueType)
{
finalMessage = string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.AreSameGivenValues,
userMessage);
message += Environment.NewLine + string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AreSameGivenValues, string.Empty).TrimEnd();
}
else
{
message += Environment.NewLine + FrameworkMessages.AreSameFailNew;
}

ThrowAssertFailed("Assert.AreSame", finalMessage);
message += FormatAlignedParameters(
(nameof(expected), expectedValue),
(nameof(actual), actualValue));

ThrowAssertFailed(callSite, message);
}

/// <inheritdoc cref="AreNotSame{T}(T, T, string?, string, string)" />
Expand Down Expand Up @@ -240,14 +261,22 @@ public static void AreNotSame<T>(T? notExpected, T? actual, string? message = ""
{
if (IsAreNotSameFailing(notExpected, actual))
{
ThrowAssertAreNotSameFailed(BuildUserMessageForNotExpectedExpressionAndActualExpression(message, notExpectedExpression, actualExpression));
ThrowAssertAreNotSameFailed(notExpected, actual, message, notExpectedExpression, actualExpression);
}
}

private static bool IsAreNotSameFailing<T>(T? notExpected, T? actual)
=> object.ReferenceEquals(notExpected, actual);

[DoesNotReturn]
private static void ThrowAssertAreNotSameFailed(string userMessage)
=> ThrowAssertFailed("Assert.AreNotSame", userMessage);
private static void ThrowAssertAreNotSameFailed<T>(T? notExpected, T? actual, string? userMessage, string notExpectedExpression, string actualExpression)
{
string callSite = FormatCallSite("Assert.AreNotSame", (nameof(notExpected), notExpectedExpression), (nameof(actual), actualExpression));
string message = string.IsNullOrEmpty(userMessage) ? string.Empty : userMessage!;
message += Environment.NewLine + FrameworkMessages.AreNotSameFailNew;
message += FormatAlignedParameters(
(nameof(notExpected), FormatValue(notExpected)),
(nameof(actual), FormatValue(actual)));
ThrowAssertFailed(callSite, message);
}
}
Loading
Loading