Skip to content

Proxy fail to create for create substitute that returns a derived class from abstract that satisfies interface implicitly #659

@siblount

Description

@siblount

Describe the bug
Castle proxy fails to create a substitute for a concrete class that derives from an abstract class and virtual members.

To Reproduce
Here's an example of what I am talking about:
Setup classes

namespace TestNSubstituteBS
{
    public abstract class AbstractIOContext
    {
        public abstract IDirectoryInfo CreateDirectoryInfo();
    }
}
namespace TestNSubstituteBS
{
    public class ConcreteIOContext : AbstractIOContext
    {
        public override XDirectoryInfo CreateDirectoryInfo() => new XDirectoryInfo();
    }
}
namespace TestNSubstituteBS
{
    public class XDirectoryInfo : IDirectoryInfo { }
}
namespace TestNSubstituteBS
{
    public interface IDirectoryInfo { }
}

Test code:

namespace TestNSubstituteBS.Tests
{
    [TestClass()]
    public class AbstractFactoryTests
    {
        [TestMethod]
        public void CreateTest()
        {
            var a = Substitute.For<ConcreteIOContext>(); // <--------------- fails here
            a.CreateDirectoryInfo().Returns(new XDirectoryInfo());
        }
    }
}

Running the above returns this error message...

Test method TestNSubstituteBS.Tests.AbstractFactoryTests.CreateTest threw exception: 
System.TypeLoadException: Return type in method 'Castle.Proxies.ConcreteIOContextProxy.CreateDirectoryInfo()' on type 'Castle.Proxies.ConcreteIOContextProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with base type method 'TestNSubstituteBS.ConcreteIOContext.CreateDirectoryInfo()'.

and stack trace...

  Stack Trace: 
TypeBuilder.CreateTypeNoLock()
TypeBuilder.CreateTypeInfo()
AbstractTypeEmitter.BuildType()
BaseClassProxyGenerator.GenerateType(String name, INamingScope namingScope)
<>c__DisplayClass13_0.<GetProxyType>b__0(CacheKey cacheKey)
SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
BaseProxyGenerator.GetProxyType()
DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
CastleDynamicProxyFactory.CreateProxyUsingCastleProxyGenerator(Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments, IInterceptor[] interceptors, ProxyGenerationOptions proxyGenerationOptions)
CastleDynamicProxyFactory.GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments, Boolean callBaseByDefault)
SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments)
Substitute.For(Type[] typesToProxy, Object[] constructorArguments)
Substitute.For[T](Object[] constructorArguments)
AbstractFactoryTests.CreateTest() line 18

As it turns out, it seems that there is an issue with the proxy when the ConcreteIOContext has the return type XDirectoryInfo, which does implement IDirectoryInfo, but not stated explicitly. If stated explicitly, the issue goes away.

namespace TestNSubstituteBS
{
    public class ConcreteIOContext : AbstractIOContext
    {
        // public override XDirectoryInfo CreateDirectoryInfo() => new XDirectoryInfo(); <-- does not work
        public override IDirectoryInfo CreateDirectoryInfo() => new XDirectoryInfo(); // <--- this does work
    }
}

The code above now makes it work without any issue.

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