NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

Trying to configure/override a generic overridden virtual method with an out parameter on a class substitute throws an exception

Open rbeurskens opened this issue 1 year ago • 2 comments

Describe the bug Trying to configure/override a generic overridden virtual method with an out parameter on a class substitute throws an exception.

System.ArgumentException : Could not find method overriding Boolean TryCreateControl[TResult](IBaseControl, IHasGridLinesDefined, TResult ByRef) on type MyControlFactory. This is most likely a bug. Please report it.
   at Castle.DynamicProxy.Internal.InvocationHelper.ObtainMethod(MethodInfo proxiedMethod, Type type)
   at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Castle.DynamicProxy.Internal.InvocationHelper.GetMethodOnType(Type type, MethodInfo proxiedMethod)
   at NSubstitute.Proxies.CastleDynamicProxy.CastleInvocationMapper.Map(IInvocation castleInvocation)
   at NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.MyControlFactoryProxy.TryCreateControl[TResult](IBaseControl control, IHasGridLinesDefined parentGrid, TResult& result)

To Reproduce

given:

public interface IControlFactory
{
    bool TryCreateControl<TResult>(IBaseControl control, IHasGridLinesDefined? parentGrid, [MaybeNullWhen(false)] out TResult result);
}
public class ControlFactory: IControlFactory
{
    public virtual bool TryCreateControl<TResult>(IBaseControl control, IHasGridLinesDefined? parentGrid, [MaybeNullWhen(false)] out TResult result) { /* ... */ }
}
public class MyControlFactory: ControlFactory
{
    // If this override is removed, code runs as expected
    public override bool TryCreateControl<TResult>(IBaseControl control, IHasGridLinesDefined? parentGrid, [MaybeNullWhen(false)] out TResult result) { /* ... */ }
}

[Test]
void MyTest()
{
    var control = Substitute.For<IStringTextBoxControl>();
    var controlFactory = Substitute.For<MyControlFactory>();
    controlFactory.Configure().TryCreateControl<object>(default, default, out var _)
          .ReturnsForAnyArgs(ci => { ci[2] = control; return true; }); // <== exception here
}

Expected behaviour Runs normally and the method returns the desired object when called

Environment:

  • NSubstitute version: 5.0.0
  • NSubstitute.Analyzers version: CSharp 1.0.16
  • Platform: .NET Framework 4.8

Additional context This might be a bug in Castle.Core or wrong usage of it, but I can't tell. If the method override in the MyControlFactory class is removed, the exception does not occur and code runs as expected

rbeurskens avatar Jun 15 '23 10:06 rbeurskens

See referenced issue in Caste.Core. I guess whenever they release a version that includes the fix, I can just use the updated Casle.Core version to resolve the problem.

rbeurskens avatar Aug 29 '23 02:08 rbeurskens

Thanks @rbeurskens . Happy to bump the Castle.Core version when a fix is included. 👍

dtchepak avatar Sep 03 '23 10:09 dtchepak