C# 7.2 introduced the possibility of using reference semantics for value types - this is done by applying the in modifier for value-type parameters in method signatures.
NSubstitute is apparently not able to generate mocks for interfaces that use these new modifiers. Given the unit tests below:
using NSubstitute;
using Xunit;
namespace SomeProject.Tests
{
public readonly struct Struct
{
}
public interface IStructByRefConsumer
{
void Consume(in Struct message);
}
public interface IStructByValueConsumer
{
void Consume(Struct message);
}
public class TempSpec
{
// Fails.
[Fact]
internal void IStructByRefConsumer_Test()
{
_ = Substitute.For<IStructByRefConsumer>();
}
// Passes.
[Fact]
internal void IStructByValueConsumer_Test()
{
_ = Substitute.For<IStructByValueConsumer>();
}
}
}
What I find is that IStructByValueConsumer_Test passes as expected, while IStructByRefConsumer_Test fails with this exception:
Test Name: SomeProject.Tests.TempSpec.IStructByRefConsumer_Test
Test FullName: SomeProject.Tests.TempSpec.IStructByRefConsumer_Test
Test Source: C:\path\to\tests\TempSpec.cs : line 30
Test Outcome: Failed
Test Duration: 0:00:22.187
Result StackTrace:
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type)
at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
at Castle.DynamicProxy.Generators.InterfaceProxyWithoutTargetGenerator.GenerateType(String typeName, Type proxyTargetType, Type[] interfaces, INamingScope namingScope)
at Castle.DynamicProxy.Generators.InterfaceProxyWithTargetGenerator.<>c__DisplayClass6_0.<GenerateCode>b__0(String n, INamingScope s)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors)
at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments, SubstituteConfig config)
at NSubstitute.Substitute.For[T](Object[] constructorArguments)
at SomeProject.Tests.TempSpec.IStructByRefConsumer_Test() in C:\path\to\tests\TempSpec.cs:line 32
Result Message: System.TypeLoadException : Signature of the body and declaration in a method implementation do not match. Type: 'Castle.Proxies.IStructByRefConsumerProxy'. Assembly: 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
This isn't an NSubstitute-specific problem. I found the same problem with Moq too:
public class TempSpec
{
// Fails.
[Fact]
internal void Struct_ByRef_Moq_Test()
{
_ = Mock.Of<IStructByRefConsumer>();
}
// Fails.
[Fact]
internal void Struct_ByRef_NSubstitute_Test()
{
_ = Substitute.For<IStructByRefConsumer>();
}
// Passes.
[Fact]
internal void Struct_ByValue_Moq_Test()
{
_ = Mock.Of<IStructByValueConsumer>();
}
// Passes.
[Fact]
internal void Struct_ByValue_NSubstitute_Test()
{
_ = Substitute.For<IStructByValueConsumer>();
}
}
Test Name: SomeProject.Tests.TempSpec.Struct_ByRef_Moq_Test
Test FullName: SomeProject.Tests.TempSpec.Struct_ByRef_Moq_Test
Test Source: : line -1
Test Outcome: Failed
Test Duration: 0:00:00.016
Result StackTrace:
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateTypeInfo()
at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.CreateType(TypeBuilder type)
at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
at Castle.DynamicProxy.Generators.InterfaceProxyWithoutTargetGenerator.GenerateType(String typeName, Type proxyTargetType, Type[] interfaces, INamingScope namingScope)
at Castle.DynamicProxy.Generators.InterfaceProxyWithTargetGenerator.<>c__DisplayClass6_0.<GenerateCode>b__0(String n, INamingScope s)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func`3 factory)
at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors)
at Moq.Mock`1.InitializeInstancePexProtected()
at Moq.Mock`1.OnGetObject()
at Moq.Mock`1.get_Object()
at Moq.Mocks.<CreateMocks>d__6`1.MoveNext()
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
at System.Linq.EnumerableExecutor`1.Execute()
at System.Linq.EnumerableQuery`1.System.Linq.IQueryProvider.Execute[TElement](Expression expression)
at System.Linq.Queryable.First[TSource](IQueryable`1 source)
at System.Linq.Queryable.First[TSource](IQueryable`1 source)
at SomeProject.Tests.TempSpec.Struct_ByRef_Moq_Test() in C:\path\to\tests\TempSpec.cs:line 33
Result Message: System.TypeLoadException : Signature of the body and declaration in a method implementation do not match. Type: 'Castle.Proxies.IStructByRefConsumerProxy'. Assembly: 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Both of these fail at:
Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors)
So I guess the bug is in Castle.Core.
@TAGC Thanks for firing the issue for Castle. It's indeed their issue 馃槈
Just to let you guys know, the Castle.Core team are considering bumping their library to (at least) .NET Standard 1.5 in order to resolve this proxy generation issue. The Moq guys are prepared to do something similar when the Castle.Core team release an updated build, so you may want to consider following suit.
Thanks a lot for the update! Let's re-open this issue to track the process.
Just to let you guys know, the Castle.Core team are considering bumping their library to (at least) .NET Standard 1.5
That should not be a big problem, as we are working on the major version now, so it's a good time to make this change.
Required list of actions:
Castle.Core dependency, increasing the version of .NET Standard if needed.Hello
Today I noticed the same issue again with a different setup:
Minimal example:
```c#
using NSubstitute;
using NUnit.Framework;
namespace DebugNUnitError
{
[TestFixture]
public class Class1
{
public interface IBar {}
public interface IFoo<T>
{
bool DoSomething(in string[] stringArray);
}
[Test]
public void TestSubstituteFor()
{
var mock = Substitute.For<IFoo<IBar>>();
}
}
}
Library and runtime versions:
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>DebugError</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
</ItemGroup>
As far as I understood the issue should be fixed since Core 2.2.2 and in the current version of NSubstitute. But I still get this error:
Castle.DynamicProxy.ProxyGenerationException : There was an error in static constructor on type Castle.Proxies.ObjectProxy. This is likely a bug in DynamicProxy. Please report it.
----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> System.TypeInitializationException : The type initializer for 'Castle.Proxies.ObjectProxy' threw an exception.
----> System.MissingMethodException : Method not found: 'Boolean Common.Network.DataProcessor.IRegister1.TryGet(System.String[] ByRef, !0 ByRef, Int32 ByRef)'.
at Castle.DynamicProxy.Internal.TypeUtil.SetStaticField(Type type, String fieldName, BindingFlags additionalFlags, Object value)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.InitializeStaticFields(Type builtType)
at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateType(String name, Type[] interfaces, INamingScope namingScope)
at Castle.DynamicProxy.Generators.ClassProxyGenerator.<>c__DisplayClass1_0.<GenerateCode>b__0(String n, INamingScope s)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass33_0.<ObtainProxyType>b__0(CacheKey _)
at Castle.Core.Internal.SynchronizedDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at Castle.DynamicProxy.Generators.BaseProxyGenerator.ObtainProxyType(CacheKey cacheKey, Func3 factory)
at Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] interfaces, ProxyGenerationOptions options)
at Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.CreateProxyUsingCastleProxyGenerator(Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments, IInterceptor[] interceptors, ProxyGenerationOptions proxyGenerationOptions)
at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments)
at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments, Boolean callBaseByDefault)
at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments)
at NSubstitute.Substitute.For(Type[] typesToProxy, Object[] constructorArguments)
at NSubstitute.Substitute.ForT
at Common.Network.DataProcessor.TestRegistry.SetUp() in D:\Projects\CSharp\projects\hello.game\source\HelloWorldGame\Common\Network\Network.unit_tests\DataProcessor\TestRegistry.cs:line 14
--TargetInvocationException
at System.RuntimeFieldHandle.SetValue(RtFieldInfo field, Object obj, Object value, RuntimeType fieldType, FieldAttributes fieldAttr, RuntimeType declaringType, Boolean& domainInitialized)
at System.Reflection.RtFieldInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
at System.Reflection.FieldInfo.SetValue(Object obj, Object value)
at Castle.DynamicProxy.Internal.TypeUtil.SetStaticField(Type type, String fieldName, BindingFlags additionalFlags, Object value)
--TypeInitializationException
--MissingMethodException
at Castle.Proxies.ObjectProxy..cctor()
```
Further information:
in modifier.IFoo.@Netzeband I think this is related to https://github.com/castleproject/Core/issues/430. It still seems it is not fixed in current CoreCLR versions (https://github.com/castleproject/Core/issues/430#issuecomment-756309040).
@dtchepak Thanks for the hint. This seems to be the same issue. So I will watch the other ticket to see, if it got fixed or not.
Most helpful comment
This isn't an NSubstitute-specific problem. I found the same problem with
Moqtoo:Both of these fail at:
So I guess the bug is in Castle.Core.