devlooped / moq

The most popular and friendly mocking framework for .NET
Other
5.94k stars 802 forks source link

Exception throw in dotnet8 (working in dotnet6) #1454

Closed p1971 closed 5 months ago

p1971 commented 10 months ago

Describe the Bug

I have a test that asserts that an extension method correctly registers all required dependencies such that clients should have everything fully registered. On dotnet8 this throws an exception. I've included a cut-down example.

Steps to Reproduce

Execute the code below - net6 works - net8 fails

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
    <PackageReference Include="Moq" Version="4.20.70" />
    <PackageReference Include="xunit" Version="2.6.4" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>
public class ExampleOfFailingTest
{
    [Fact]
    public void ServiceProviderShouldResolveTheInterface()
    {    
        IServiceCollection subject = new ServiceCollection();

        // Create a mocked interface
        Mock<ITestInterface<TestClass>> mockInterface = new();

        // add it to the service collection
        subject.TryAddSingleton(typeof(ITestInterface<TestClass>), mockInterface.Object.GetType());

        // build the service provider
        ServiceProvider serviceProvider = subject.BuildServiceProvider();

        // now get an instance of the service
        Assert.NotNull(serviceProvider.GetService<ITestInterface<TestClass>>());
    }
}

public class TestClass
{
}

public interface ITestInterface<in T>
    where T : class
{
    Task DoSomeWork(T request);
}

Expected Behavior

Should also pass on dotnet8

Exception with Stack Trace

System.InvalidOperationException
Unable to resolve service for type 'Castle.DynamicProxy.IInterceptor[]' while attempting to activate 'Castle.Proxies.ITestInterface`1Proxy'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at MoqTest.ExampleOfFailingTest.ServiceProviderShouldResolveTheInterface() in E:\temp\src\moq\MoqTest\MoqTest\Class1.cs:line 26
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Version Info

Moq - 4.20.70

snowinmars commented 5 months ago

See https://github.com/dotnet/runtime/issues/102888

kzu commented 5 months ago

If you want the service collection to return your previously constructed mock from its GetService, you need to register the function to retrieve it, not the mock type which the container doesn't know how to instantiate:

subject.TryAddSingleton(typeof(ITestInterface<TestClass>), sp => mockInterface.Object);