Skip to content

Commit

Permalink
Assert.AreEqual allow IEquatable<T> for actual and expected (#2382)
Browse files Browse the repository at this point in the history
  • Loading branch information
Evangelink authored Feb 19, 2024
1 parent e62e95c commit 7bdff57
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
99 changes: 99 additions & 0 deletions src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,105 @@ public static void AreEqual<T>(T? expected, T? actual, IEqualityComparer<T>? com
ThrowAssertFailed("Assert.AreEqual", finalMessage);
}

/// <summary>
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
/// The equality is computed using the default <see cref="System.Collections.Generic.EqualityComparer{T}"/>.
/// </summary>
/// <typeparam name="T">
/// The type of values to compare.
/// </typeparam>
/// <param name="expected">
/// The first value to compare. This is the value the tests expects.
/// </param>
/// <param name="actual">
/// The second value to compare. This is the value produced by the code under test.
/// </param>
/// <exception cref="AssertFailedException">
/// Thrown if <paramref name="expected"/> is not equal to <paramref name="actual"/>.
/// </exception>
public static void AreEqual<T>(IEquatable<T>? expected, IEquatable<T>? actual)
=> AreEqual(expected, actual, string.Empty, null);

/// <summary>
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
/// The equality is computed using the default <see cref="System.Collections.Generic.EqualityComparer{T}"/>.
/// </summary>
/// <typeparam name="T">
/// The type of values to compare.
/// </typeparam>
/// <param name="expected">
/// The first value to compare. This is the value the tests expects.
/// </param>
/// <param name="actual">
/// The second value to compare. This is the value produced by the code under test.
/// </param>
/// <param name="message">
/// The message to include in the exception when <paramref name="actual"/>
/// is not equal to <paramref name="expected"/>. The message is shown in
/// test results.
/// </param>
/// <exception cref="AssertFailedException">
/// Thrown if <paramref name="expected"/> is not equal to
/// <paramref name="actual"/>.
/// </exception>
public static void AreEqual<T>(IEquatable<T>? expected, IEquatable<T>? actual, string? message)
=> AreEqual(expected, actual, message, null);

/// <summary>
/// Tests whether the specified values are equal and throws an exception
/// if the two values are not equal.
/// The equality is computed using the default <see cref="System.Collections.Generic.EqualityComparer{T}"/>.
/// </summary>
/// <typeparam name="T">
/// The type of values to compare.
/// </typeparam>
/// <param name="expected">
/// The first value to compare. This is the value the tests expects.
/// </param>
/// <param name="actual">
/// The second value to compare. This is the value produced by the code under test.
/// </param>
/// <param name="message">
/// The message to include in the exception when <paramref name="actual"/>
/// is not equal to <paramref name="expected"/>. The message is shown in
/// test results.
/// </param>
/// <param name="parameters">
/// An array of parameters to use when formatting <paramref name="message"/>.
/// </param>
/// <exception cref="AssertFailedException">
/// Thrown if <paramref name="expected"/> is not equal to
/// <paramref name="actual"/>.
/// </exception>
public static void AreEqual<T>(IEquatable<T>? expected, IEquatable<T>? actual, string? message, params object?[]? parameters)
{
if (actual?.Equals(expected) == true)
{
return;
}

string userMessage = BuildUserMessage(message, parameters);
string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType())
? string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.AreEqualDifferentTypesFailMsg,
userMessage,
ReplaceNulls(expected),
expected.GetType().FullName,
ReplaceNulls(actual),
actual.GetType().FullName)
: string.Format(
CultureInfo.CurrentCulture,
FrameworkMessages.AreEqualFailMsg,
userMessage,
ReplaceNulls(expected),
ReplaceNulls(actual));

ThrowAssertFailed("Assert.AreEqual", finalMessage);
}

/// <summary>
/// Tests whether the specified values are unequal and throws an exception
/// if the two values are equal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expe
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message, params object?[]? parameters) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(System.IEquatable<T>? expected, System.IEquatable<T>? actual) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(System.IEquatable<T>? expected, System.IEquatable<T>? actual, string? message) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(System.IEquatable<T>? expected, System.IEquatable<T>? actual, string? message, params object?[]? parameters) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(T? expected, T? actual) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(T? expected, T? actual, string? message) -> void
static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual<T>(T? expected, T? actual, string? message, params object?[]? parameters) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,18 @@ public void AreEqualStringIgnoreCaseCultureInfoMessageParametersNullabilityPostC
_ = cultureInfo.Calendar; // no warning
}

public void AreEqualUsingCustomIEquatable()
{
var instanceOfA = new A { Id = "SomeId" };
var instanceOfB = new B { Id = "SomeId" };

// This call works because B implements IEquatable<A>
Assert.AreEqual(instanceOfA, instanceOfB);

// This one doesn't work
VerifyThrows(() => Assert.AreEqual(instanceOfB, instanceOfA));
}

private CultureInfo? GetCultureInfo() => CultureInfo.CurrentCulture;

private class TypeOverridesEquals
Expand Down Expand Up @@ -426,4 +438,32 @@ public override int GetHashCode(TypeOverridesEquals obj)
throw new NotImplementedException();
}
}

private class A : IEquatable<A>
{
public string Id { get; set; } = string.Empty;

public bool Equals(A? other)
=> other?.Id == Id;

public override bool Equals(object? obj)
=> Equals(obj as A);

public override int GetHashCode()
=> Id.GetHashCode() + 123;
}

private class B : IEquatable<A>
{
public string Id { get; set; } = string.Empty;

public override bool Equals(object? obj)
=> Equals(obj as A);

public bool Equals(A? other)
=> other?.Id == Id;

public override int GetHashCode()
=> Id.GetHashCode() + 1234;
}
}

0 comments on commit 7bdff57

Please sign in to comment.