From 0a796d6964b1f89840284379a8b7f78e27623ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Wed, 18 Feb 2026 09:40:33 +0100 Subject: [PATCH] Improve Assert.AreSame message for nulls --- .../Assertions/Assert.AreSame.cs | 43 +++++++++--- .../Resources/FrameworkMessages.resx | 9 +++ .../Resources/xlf/FrameworkMessages.cs.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.de.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.es.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.fr.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.it.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.ja.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.ko.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.pl.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.pt-BR.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.ru.xlf | 15 ++++ .../Resources/xlf/FrameworkMessages.tr.xlf | 15 ++++ .../xlf/FrameworkMessages.zh-Hans.xlf | 15 ++++ .../xlf/FrameworkMessages.zh-Hant.xlf | 15 ++++ .../Assertions/AssertTests.AreSame.cs | 68 ++++++++++++++++++- 16 files changed, 303 insertions(+), 12 deletions(-) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs index 93be91c7fd..048d7197e6 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs @@ -80,9 +80,13 @@ internal void ComputeAssertion(string expectedExpression, string actualExpressio public readonly struct AssertAreNotSameInterpolatedStringHandler { 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) { @@ -95,7 +99,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()); } } @@ -187,14 +191,22 @@ private static bool IsAreSameFailing(T? expected, T? actual) [DoesNotReturn] private static void ThrowAssertAreSameFailed(T? expected, T? actual, string userMessage) { - string finalMessage = userMessage; - if (expected is ValueType && actual is ValueType) - { - finalMessage = string.Format( + string finalMessage = expected is null + ? string.Format( CultureInfo.CurrentCulture, - FrameworkMessages.AreSameGivenValues, - userMessage); - } + FrameworkMessages.AreSameExpectedIsNull, + userMessage) + : actual is null + ? string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreSameActualIsNull, + userMessage) + : expected is ValueType && actual is ValueType + ? string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreSameGivenValues, + userMessage) + : userMessage; ThrowAssertFailed("Assert.AreSame", finalMessage); } @@ -240,7 +252,7 @@ public static void AreNotSame(T? notExpected, T? actual, string? message = "" { if (IsAreNotSameFailing(notExpected, actual)) { - ThrowAssertAreNotSameFailed(BuildUserMessageForNotExpectedExpressionAndActualExpression(message, notExpectedExpression, actualExpression)); + ThrowAssertAreNotSameFailed(notExpected, actual, BuildUserMessageForNotExpectedExpressionAndActualExpression(message, notExpectedExpression, actualExpression)); } } @@ -248,6 +260,15 @@ private static bool IsAreNotSameFailing(T? notExpected, T? actual) => object.ReferenceEquals(notExpected, actual); [DoesNotReturn] - private static void ThrowAssertAreNotSameFailed(string userMessage) - => ThrowAssertFailed("Assert.AreNotSame", userMessage); + private static void ThrowAssertAreNotSameFailed(T? notExpected, T? actual, string userMessage) + { + string finalMessage = notExpected is null && actual is null + ? string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreNotSameBothNull, + userMessage) + : userMessage; + + ThrowAssertFailed("Assert.AreNotSame", finalMessage); + } } diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx index 537b9331a7..63dd637faf 100644 --- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx +++ b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx @@ -162,9 +162,18 @@ Expected a difference greater than <{3}> between expected value <{1}> and actual value <{2}>. {0} + + Expected is <null>. {0} + + + Actual is <null>. {0} + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} + + Both values are <null>. {0} + Both collections are empty. {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf index 02a8e5a3bc..59888dabc3 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf @@ -78,6 +78,21 @@ Očekáván rozdíl, který je větší jak <{3}> mezi očekávanou hodnotou <{1}> a aktuální hodnotou <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Nevkládejte hodnotu typů do AreSame(). Hodnoty převedené do typu Object už nebudou nikdy stejné. Zvažte použití AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf index b240c09df6..ce89eb4df1 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf @@ -78,6 +78,21 @@ Es wurde eine Differenz größer als <{3}> zwischen dem erwarteten Wert <{1}> und dem tatsächlichen Wert <{2}> erwartet. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Übergeben Sie keine Werttypen an AreSame(). In ein Objekt konvertierte Werte sind niemals identisch. Verwenden Sie stattdessen AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf index 427b030537..b483d3db42 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf @@ -78,6 +78,21 @@ Se esperaba una diferencia mayor que <{3}> entre el valor esperado <{1}> y el valor actual <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} No pase tipos de valor a AreSame(). Los valores convertidos a Object no serán nunca iguales. Considere el uso de AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf index 0e46d1985a..519fbdf8d9 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf @@ -78,6 +78,21 @@ Différence attendue supérieure à <{3}> comprise entre la valeur attendue <{1}> et la valeur réelle <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Ne passez pas de types valeur à AreSame(). Les valeurs converties en Object ne seront plus jamais les mêmes. Si possible, utilisez AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf index bfdc4c7ee6..62c16203d0 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf @@ -78,6 +78,21 @@ Prevista una differenza maggiore di <{3}> tra il valore previsto <{1}> e il valore effettivo <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Non passare tipi valore a AreSame(). I valori convertiti in Object non saranno mai uguali. Usare AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf index bdced96434..fccca2c24a 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf @@ -78,6 +78,21 @@ 指定する値 <{1}> と実際の値 <{2}> との間には、<{3}> を超える差が必要です。{0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} AreSame() には値型を渡すことはできません。オブジェクトに変換された値が同じにはなりません。AreEqual() を使用することを検討してください。{0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf index 6d9292a56e..8ec53672e2 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf @@ -78,6 +78,21 @@ 예상 값 <{1}>과(와) 실제 값 <{2}>의 차이가 <{3}>보다 커야 합니다. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} AreSame()에 값 형식을 전달하면 안 됩니다. Object로 변환된 값은 동일한 값으로 간주되지 않습니다. AreEqual()을 사용해 보세요. {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf index c50ff5897a..63940cdc35 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf @@ -78,6 +78,21 @@ Oczekiwano różnicy większej niż <{3}> pomiędzy oczekiwaną wartością <{1}> a rzeczywistą wartością <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Nie przekazuj typów wartości do metody AreSame(). Wartości przekonwertowane na typ Object nigdy nie będą takie same. Rozważ użycie metody AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf index faf04cc886..3472a3d091 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf @@ -78,6 +78,21 @@ Esperada uma diferença maior que <{3}> entre o valor esperado <{1}> e o valor real <{2}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Não passe tipos de valores para AreSame(). Os valores convertidos para Object nunca serão os mesmos. Considere usar AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf index e39af70016..b9a6a3c49a 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf @@ -78,6 +78,21 @@ Между ожидаемым значением <{1}> и фактическим значением <{2}> требуется разница более чем <{3}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} Не передавайте типы значений в функцию AreSame(). Значения, преобразованные в Object, никогда не будут одинаковыми. Попробуйте использовать AreEqual(). {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf index 3a24012694..a7cd1e6c96 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf @@ -78,6 +78,21 @@ Beklenen değer <{1}> ile gerçek değer <{2}> arasında, şundan büyük olan fark bekleniyor: <{3}>. {0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} AreSame()'e değer türleri geçirmeyin. Object olarak dönüştürülen değerler asla aynı olamayacak. AreEqual() kullanmayı düşün. {0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf index 94100054a3..c9cc3b1a59 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf @@ -78,6 +78,21 @@ 预期值 <{1}> 和实际值 <{2}> 之间的差应大于 <{3}>。{0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} 不要向 AreSame() 传递值类型。转换为 Object 的值将永远不会相等。请考虑使用 AreEqual()。{0} diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf index 3d758f945a..9b7c77e205 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf @@ -78,6 +78,21 @@ 預期值 <{1}> 和實際值 <{2}> 之間的預期差異大於 <{3}>。{0} + + Both values are <null>. {0} + Both values are <null>. {0} + + + + Actual is <null>. {0} + Actual is <null>. {0} + + + + Expected is <null>. {0} + Expected is <null>. {0} + + Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). {0} 不要將實值型別傳遞給 AreSame()。轉換成 Object 的值從此不再一樣。請考慮使用 AreEqual()。{0} diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs index bd97809016..fd7569a444 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// 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; @@ -68,6 +68,48 @@ public void AreSame_InterpolatedString_BothAreValueTypes_ShouldFailWithSpecializ action.Should().Throw().WithMessage("Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). 'expected' expression: '1', 'actual' expression: '1'. User-provided message System.Object"); } + public void AreSame_ExpectedNull_ShouldFailWithNullMessage() + { + object? expected = null; + Action action = () => Assert.AreSame(expected, new object()); + action.Should().Throw().WithMessage("Assert.AreSame failed. Expected is . 'expected' expression: 'expected', 'actual' expression: 'new object()'."); + } + + public void AreSame_ActualNull_ShouldFailWithNullMessage() + { + object? actual = null; + Action action = () => Assert.AreSame(new object(), actual); + action.Should().Throw().WithMessage("Assert.AreSame failed. Actual is . 'expected' expression: 'new object()', 'actual' expression: 'actual'."); + } + + public void AreSame_StringMessage_ExpectedNull_ShouldFailWithNullMessage() + { + object? expected = null; + Action action = () => Assert.AreSame(expected, new object(), "User-provided message"); + action.Should().Throw().WithMessage("Assert.AreSame failed. Expected is . 'expected' expression: 'expected', 'actual' expression: 'new object()'. User-provided message"); + } + + public void AreSame_StringMessage_ActualNull_ShouldFailWithNullMessage() + { + object? actual = null; + Action action = () => Assert.AreSame(new object(), actual, "User-provided message"); + action.Should().Throw().WithMessage("Assert.AreSame failed. Actual is . 'expected' expression: 'new object()', 'actual' expression: 'actual'. User-provided message"); + } + + public void AreSame_InterpolatedString_ExpectedNull_ShouldFailWithNullMessage() + { + object? expected = null; + Action action = () => Assert.AreSame(expected, new object(), $"User-provided message {new object().GetType()}"); + action.Should().Throw().WithMessage("Assert.AreSame failed. Expected is . 'expected' expression: 'expected', 'actual' expression: 'new object()'. User-provided message System.Object"); + } + + public void AreSame_InterpolatedString_ActualNull_ShouldFailWithNullMessage() + { + object? actual = null; + Action action = () => Assert.AreSame(new object(), actual, $"User-provided message {new object().GetType()}"); + action.Should().Throw().WithMessage("Assert.AreSame failed. Actual is . 'expected' expression: 'new object()', 'actual' expression: 'actual'. User-provided message System.Object"); + } + public void AreNotSame_PassSameObject_ShouldFail() { object o = new(); @@ -100,4 +142,28 @@ public async Task AreNotSame_InterpolatedString_PassSameObject_ShouldFail() (await action.Should().ThrowAsync()).WithMessage($"Assert.AreNotSame failed. 'notExpected' expression: 'o', 'actual' expression: 'o'. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); o.WasToStringCalled.Should().BeTrue(); } + + public void AreNotSame_BothNull_ShouldFailWithNullMessage() + { + object? notExpected = null; + object? actual = null; + Action action = () => Assert.AreNotSame(notExpected, actual); + action.Should().Throw().WithMessage("Assert.AreNotSame failed. Both values are . 'notExpected' expression: 'notExpected', 'actual' expression: 'actual'."); + } + + public void AreNotSame_StringMessage_BothNull_ShouldFailWithNullMessage() + { + object? notExpected = null; + object? actual = null; + Action action = () => Assert.AreNotSame(notExpected, actual, "User-provided message"); + action.Should().Throw().WithMessage("Assert.AreNotSame failed. Both values are . 'notExpected' expression: 'notExpected', 'actual' expression: 'actual'. User-provided message"); + } + + public void AreNotSame_InterpolatedString_BothNull_ShouldFailWithNullMessage() + { + object? notExpected = null; + object? actual = null; + Action action = () => Assert.AreNotSame(notExpected, actual, $"User-provided message {new object().GetType()}"); + action.Should().Throw().WithMessage("Assert.AreNotSame failed. Both values are . 'notExpected' expression: 'notExpected', 'actual' expression: 'actual'. User-provided message System.Object"); + } }