From da6c1744707bd10ae02a34a4446727606d5223d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mihnea=20R=C4=83dulescu?= <29178174+mihnea-radulescu@users.noreply.github.com> Date: Tue, 14 May 2024 15:13:02 +0300 Subject: [PATCH] Arg.Any() does not match arguments passed by reference (#787) (#811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mihnea Rădulescu <> --- src/NSubstitute/Core/CallSpecification.cs | 11 ++++++++--- .../ReceivedCalls.cs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/NSubstitute/Core/CallSpecification.cs b/src/NSubstitute/Core/CallSpecification.cs index 0dfa04cf..b0018439 100644 --- a/src/NSubstitute/Core/CallSpecification.cs +++ b/src/NSubstitute/Core/CallSpecification.cs @@ -77,7 +77,7 @@ internal static bool TypesAreAllEquivalent(Type[] aArgs, Type[] bArgs) if (first.IsGenericType && second.IsGenericType && first.GetGenericTypeDefinition() == second.GetGenericTypeDefinition()) { - // both are the same generic type. If their GenericTypeArguments match then they are equivalent + // both are the same generic type. If their GenericTypeArguments match then they are equivalent if (!TypesAreAllEquivalent(first.GenericTypeArguments, second.GenericTypeArguments)) { return false; @@ -85,8 +85,13 @@ internal static bool TypesAreAllEquivalent(Type[] aArgs, Type[] bArgs) continue; } - var areEquivalent = first.IsAssignableFrom(second) || second.IsAssignableFrom(first) || - typeof(Arg.AnyType).IsAssignableFrom(first) || typeof(Arg.AnyType).IsAssignableFrom(second); + var areAssignable = first.IsAssignableFrom(second) || second.IsAssignableFrom(first); + var areAnyTypeAssignable = typeof(Arg.AnyType).IsAssignableFrom(first) || + typeof(Arg.AnyType).IsAssignableFrom(second); + var areByRefAnyTypeAssignable = first.IsByRef && second.IsByRef && + (typeof(Arg.AnyType).IsAssignableFrom(first.GetElementType()) || + typeof(Arg.AnyType).IsAssignableFrom(second.GetElementType())); + var areEquivalent = areAssignable || areAnyTypeAssignable || areByRefAnyTypeAssignable; if (!areEquivalent) return false; } return true; diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index 3edbe6ff..4a664d9d 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -316,6 +316,16 @@ public void Throw_when_negative_min_range_given() StringAssert.Contains("minInclusive must be >= 0, but was -1.", ex.Message); } + [Test] + public void Works_with_byref_generic_parameters() + { + IMyService service = Substitute.For(); + MyArgument arg = new(); + service.MyMethod(ref arg); + + service.Received().MyMethod(ref Arg.Any()); + } + public interface ICar { void Start(); @@ -329,4 +339,11 @@ public interface ICar float GetCapacityInLitres(); event Action Started; } + + public interface IMyService + { + void MyMethod(ref T argument); + } + + public class MyArgument { } } \ No newline at end of file