nunit-console icon indicating copy to clipboard operation
nunit-console copied to clipboard

DependencyInjection via Autofac not working properly using NUnit console (or testcentric GUI)

Open Sputnik24 opened this issue 1 year ago • 7 comments

In my NUnit-Test I register some services using Autofac library:

[Test]
public void Test() {
    var builder = new ContainerBuilder();
    builder.RegisterType(typeof(FakeServiceNoConfig)).AsSelf().As<IStartable>().SingleInstance();
    IContainer container = builder.Build();

    Assert.IsTrue(container.TryResolve(out IFakeServiceNoConfig? service));
    container.Dispose();
}
public class FakeServiceNoConfig : IFakeServiceNoConfig, IStartable, IDisposable {
    public void Start() { }

    public void Dispose() { }
}

public interface IFakeServiceNoConfig { }

When I run the test using testcentric GUI or nunit3-console.exe or dotnet nunit3-netcore-console.dll I receive the following exception

System.ArgumentException : The type 'UnitTests.ServiceFactoryTest.FakeServiceNoConfig' is not assignable to service 'Autofac.IStartable'.

When I remove the IStarble form the builder, I get:

1) Failed : UnitTests.ServiceFactoryTests.ServiceWithoutConfig
  Expected: True
  But was:  False

When I run the same using dotnet test, the test passes.

Is this an issue with NUnit Console/GUI or do I do something wrong?

Thanks a lot Daniel

Sputnik24 avatar Jul 27 '23 12:07 Sputnik24

Thanks for reporting this @Sputnik24

To help us out, are you able to share a csproj example that would show can the package versions and runtime for your example?

EDIT: Another option for sharing this info could be to put up a PR for your repro in our https://github.com/nunit/nunit-console.issues repo

stevenaw avatar Jul 27 '23 22:07 stevenaw

Thanks for your comment @stevenaw

I further investigated the issue and uploaded a simplified solution which reproduced the issue: https://github.com/Sputnik24/AutofacNUnit

Details:

  • There is a ServiceFactory in each class project. The implementation of the ServiceFactories is the same.
  • The difference is that AutofacNUnitLib.ServiceFactory is in the same project as the FakeService, where AutofacNUnitTest.ServiceFactory is in a different project than the FakeService.
  • I implemented two tests: FakeServiceSameProject, which uses AutofacNUnitLib.ServiceFactory and FakeServiceDiffProject which uses the other.
  • To get additional information, the ServiceFactory in AutofacNUnitTest prints the FullName of the type to register and its iterfaces to the NUnit console.

Behavior:

  • Running the test in my IDE (Rider): Both tests succeed
  • Running the test using dotnet test: Both tests succed
  • Running the test using either nunit3-console.exe or dotnet nunit3-netcore-console.dll: FakeServiceSameProject succeeds but FakeServiceDiffProject fails with:
C:\dev\nunit\nunit3-console.exe .\AutofacNUnitTest.dll
NUnit Console 3.16.2 (Release)
Copyright (c) 2022 Charlie Poole, Rob Prouse
Freitag, 28. Juli 2023 08:34:25

Runtime Environment
   OS Version: Microsoft Windows NT 6.2.9200.0
   Runtime: .NET Framework CLR v4.0.30319.42000

Test Files
    .\AutofacNUnitTest.dll

AutofacNUnitLib.FakeService:
AutofacNUnitLib.IFakeService
Autofac.IStartable
System.IDisposable

Errors, Failures and Warnings

1) Error : AutofacNUnitTest.ServiceFactoryTests.FakeServiceDiffProjects
System.ArgumentException : The type 'AutofacNUnitLib.FakeService' is not assignable to service 'Autofac.IStartable'.
   at Autofac.Builder.RegistrationBuilder.CreateRegistration(Guid id, RegistrationData data, IInstanceActivator activator, IResolvePipelineBuilder pipelineBuilder, Service[] services, IComponentRegistration target)
   at Autofac.Builder.RegistrationBuilder.CreateRegistration[TLimit,TActivatorData,TSingleRegistrationStyle](IRegistrationBuilder`3 builder)
   at Autofac.Builder.RegistrationBuilder.RegisterSingleComponent[TLimit,TActivatorData,TSingleRegistrationStyle](IComponentRegistryBuilder cr, IRegistrationBuilder`3 builder)
   at Autofac.RegistrationExtensions.<>c__DisplayClass40_0.<RegisterType>b__0(IComponentRegistryBuilder cr)
   at Autofac.ContainerBuilder.Build(IComponentRegistryBuilder componentRegistry, Boolean excludeDefaultModules)
   at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
   at AutofacNUnitTest.ServiceFactory.CreateContainer(String typeName, String interfaceName) in C:\dev\repos\AutofacNUnit\AutofacNUnitTest\ServiceFactory.cs:line 28
   at AutofacNUnitTest.ServiceFactoryTests.FakeServiceDiffProjects() in C:\dev\repos\AutofacNUnit\AutofacNUnitTest\ServiceFactoryTests.cs:line 18
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

Run Settings
    DisposeRunners: True
    WorkDirectory: C:\dev\repos\AutofacNUnit\AutofacNUnitTest\bin\Debug\net7.0
    ImageRuntimeVersion: 4.0.30319
    ImageTargetFrameworkName: .NETCoreApp,Version=v7.0
    ImageRequiresX86: False
    ImageRequiresDefaultAppDomainAssemblyResolver: False
    TargetRuntimeFramework: netcore-7.0
    NumberOfTestWorkers: 8

Test Run Summary
  Overall result: Failed
  Test Count: 2, Passed: 1, Failed: 1, Warnings: 0, Inconclusive: 0, Skipped: 0
    Failed Tests - Failures: 0, Errors: 1, Invalid: 0
  Start time: 2023-07-28 06:34:26Z
    End time: 2023-07-28 06:34:28Z
    Duration: 2.661 seconds

Details on ServiceFactory: The intention is to register type and interface given by its fullname as string (read from a config). Owing to lazy loading of assemblies the assembly containing this type/interface may not be loaded and is not found by Type.GetType(). Therefore, I implemented the FindAssemblyInBin loading all dlls from the executing folder. This works. With my debugger I see that foundType and foundInterface are correct and implement the correct interfaces (if they are null, RegisterType() would fail anyway). In the printout of the foundType you also see that the type is correct and that Autofac.IStartable is implemented.

Removing As<IStartable> or reducing it even to builder.Registertype(foundType).SingleInstance() doesn't work, either. In this case, the Assert.IsTrue fails as it cannot resolve the type, though it is registered.

Sputnik24 avatar Jul 28 '23 06:07 Sputnik24