Skip to content

Assert.Equivalent throws NotSupportedException for types with getters returning ByRefLike types #3088

@rabuckley

Description

@rabuckley

The issue can be reproduced with the following test cases

#if NET6_0_OR_GREATER
public sealed class ByRefLikeParameters
{
    [Fact]
    public void Equivalent()
    {
        var expected = new RefParameterClass(42.ToString());
        var actual = new RefParameterClass(42.ToString());

        Assert.Equivalent(expected, actual);
    }

    [Fact]
    public void NotEquivalent()
    {
        var expected = new RefParameterClass2(42.ToString());
        var actual = new RefParameterClass2(2112.ToString());

        var ex = Record.Exception(() => Assert.Equivalent(expected, actual));

        Assert.IsType<EquivalentException>(ex);
        Assert.Equal(
            "Assert.Equivalent() Failure: Mismatched value on member 'Value'" + Environment.NewLine +
            "Expected: \"42\"" + Environment.NewLine +
            "Actual:   \"2112\"",
            ex.Message
        );
    }

    public sealed class RefParameterClass(string value)
    {
        public ReadOnlySpan<char> Value => value.AsSpan();
    }

    public sealed class RefParameterClass2(string value)
    {
        public ReadOnlySpan<char> ValueSpan => value.AsSpan();

        // Added so `NotEquivalent` has a getter to check.
        public string Value => value;
    }
}
#endif

which fail with

EquivalenceAssertsTests+ByRefLikeParameters.NotEquivalent [FAIL]
      Assert.IsType() Failure: Value is not the exact type
      Expected: typeof(Xunit.Sdk.EquivalentException)
      Actual:   typeof(System.NotSupportedException)
      Stack Trace:
        src\xunit.v3.assert.tests\Asserts\EquivalenceAssertsTests.cs(1991,0): at EquivalenceAssertsTests.ByRefLikeParameters.NotEquivalent()

EquivalenceAssertsTests+ByRefLikeParameters.Equivalent [FAIL]
  System.NotSupportedException : Specified method is not supported.
  Stack Trace:
       at System.Reflection.RuntimeMethodInfo.ThrowNoInvokeException()
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
       at System.Reflection.PropertyInfo.GetValue(Object obj)
    src\xunit.v3.assert.tests\Asserts\EquivalenceAssertsTests.cs(1978,0): at EquivalenceAssertsTests.ByRefLikeParameters.Equivalent()

The exception is thrown on invocation of the returned getter func.

This can be fixed by excluding ByRefLike return types from the getters dictionary

diff --git a/Sdk/AssertHelper.cs b/Sdk/AssertHelper.cs
index ca03cc2..9413079 100644
--- a/Sdk/AssertHelper.cs
+++ b/Sdk/AssertHelper.cs
@@ -136,6 +136,9 @@ static Dictionary<string, Func<object, object>> GetGettersForType(Type type) =>
 							&& p.GetMethod != null
 							&& p.GetMethod.IsPublic
 							&& !p.GetMethod.IsStatic
+#if NET6_0_OR_GREATER
+							&& !p.GetMethod.ReturnType.IsByRefLike
+#endif
 							&& p.GetIndexParameters().Length == 0
 							&& !p.GetCustomAttributes(typeof(ObsoleteAttribute)).Any()
 							&& !p.GetMethod.GetCustomAttributes(typeof(ObsoleteAttribute)).Any()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions