I know this is an old question, but since I stumbled upon it I figured I'd weigh in with a new solution that's available (at least in xunit 2.3.1 in a .net Core 2.0 solution).
I'm not sure when it was introduced, but there is now an overloaded form of .Equal
that accepts an instance of IEqualityComparer<T>
as the third parameter. You can create a custom comparer in your unit test without polluting your code with it.
The following code can be invoked like this: Assert.Equal(expectedParameters, parameters, new CustomComparer<ParameterValue>());
XUnit natively appears to stop processing a test as soon as a failure is encountered, so throwing a new EqualException
from within our comparer seems to be in line with how XUnit works out of the box.
public class CustomComparer<T> : IEqualityComparer<T>
{
public bool Equals(T expected, T actual)
{
var props = typeof(T).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
var expectedValue = prop.GetValue(expected, null);
var actualValue = prop.GetValue(actual, null);
if (!expectedValue.Equals(actualValue))
{
throw new EqualException($"A value of \"{expectedValue}\" for property \"{prop.Name}\"",
$"A value of \"{actualValue}\" for property \"{prop.Name}\"");
}
}
return true;
}
public int GetHashCode(T parameterValue)
{
return Tuple.Create(parameterValue).GetHashCode();
}
}
Edit: I found that comparing the actual and expected values with !=
was not effective for certain types (I'm sure there's a better explanation involving the difference between reference types and value types, but that's not for today). I updated the code to use the .Equals
method to compare the two values and that seems to work much better.
Assert.Same()
compares by reference; it asserts thatObj1
andObj2
are the same object rather than just looking the same. – Anatomize