Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 7, 2026

IsEquivalentTo throws InvalidOperationException when comparing records with Type properties. RuntimeType.DeclaringMethod (among others) throws when accessed on non-generic-parameter types, and the structural comparison walks all public properties.

public record Record(Type Type);

var r = new Record(typeof(string));
await Assert.That(r).IsEquivalentTo(r); // throws InvalidOperationException

Changes

  • TypeHelper.IsPrimitiveOrWellKnownType: Added typeof(Type).IsAssignableFrom(type) and typeof(MemberInfo).IsAssignableFrom(type) so reflection types are compared by equality rather than structurally decomposed. Uses IsAssignableFrom to catch runtime subtypes like RuntimeType.
  • Regression tests: Added IsEquivalentTo_TypeProperty_Tests covering same-instance, equal, and different Type property scenarios.
Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: InvalidOperationException when using IsEquivalentTo for records with Type properties</issue_title>
<issue_description>### Description

This will throw InvalidOperationException:

public record Record(Type Type);

public class Foo
{
    [Test]
    public async Task Bar()
    {
        var r = new Record(typeof(string));
        await Assert.That(r).IsEquivalentTo(r);
    }
}
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="TUnit" Version="1.13.11" />
    </ItemGroup>

</Project>

Expected Behavior

The test should pass

Actual Behavior

Test fails

Steps to Reproduce

See description

TUnit Version

1.13.11

.NET Version

net10.0

Operating System

macOS

IDE / Test Runner

JetBrains Rider

Error Output / Stack Trace

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.InvalidOperationException: Method may only be called on a Type for which Type.IsGenericParameter is true.
   at System.RuntimeType.get_DeclaringMethod()
   at InvokeStub_RuntimeType.get_DeclaringMethod(Object, Object, IntPtr*)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at TUnit.Assertions.Conditions.Helpers.ReflectionHelper.GetMemberValue(Object obj, MemberInfo member)
   at TUnit.Assertions.Conditions.StructuralEquivalencyAssertion`1.CompareObjects(Object actual, Object expected, String path, HashSet`1 visitedActual, HashSet`1 visitedExpected)
   at TUnit.Assertions.Conditions.StructuralEquivalencyAssertion`1.CompareObjects(Object actual, Object expected, String path, HashSet`1 visitedActual, HashSet`1 visitedExpected)
   at TUnit.Assertions.Conditions.StructuralEquivalencyAssertion`1.CheckAsync(EvaluationMetadata`1 metadata)
   at TUnit.Assertions.Core.Assertion`1.CreateMetadataAndCheckAsync(TValue value, Exception exception)
   at TUnit.Assertions.Core.Assertion`1.ExecuteCoreAsync()
   at TUnit.Assertions.Core.Assertion`1.AssertAsync()
   at Foo.Bar() in /Users/alex/Projects/ConsoleApp1/ConsoleApp1/Program.cs:line 9

Additional Context

No response

IDE-Specific Issue?

  • I've confirmed this issue occurs when running via dotnet test or dotnet run, not just in my IDE</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…ith Type properties

Treat System.Type and System.Reflection.MemberInfo as well-known types in
structural equivalency comparisons, comparing by equality rather than
structural decomposition. This prevents crashes from Type properties like
DeclaringMethod that throw when accessed on non-generic-parameter types.

Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix InvalidOperationException in IsEquivalentTo for records Treat Type and MemberInfo as well-known types in structural equivalency comparisons Feb 7, 2026
Copilot AI requested a review from thomhurst February 7, 2026 21:27
@thomhurst thomhurst marked this pull request as ready for review February 7, 2026 21:31
@thomhurst thomhurst enabled auto-merge (squash) February 7, 2026 21:31
@claude
Copy link
Contributor

claude bot commented Feb 7, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

This is a well-crafted bug fix that:

  • Correctly addresses the root cause by treating Type and MemberInfo as well-known types
  • Uses IsAssignableFrom appropriately to catch runtime subtypes like RuntimeType
  • Includes comprehensive regression tests covering key scenarios
  • Makes a minimal, surgical change without over-engineering
  • Complies with all CLAUDE.md rules (dual-mode not applicable for assertions, no public API changes requiring snapshots, AOT-compatible, no performance concerns)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: InvalidOperationException when using IsEquivalentTo for records with Type properties

2 participants