Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using SimpleInjector in integration tests via WebApplicationFactory #29

Open
skuzminoff opened this issue Jun 9, 2022 · 7 comments
Open

Comments

@skuzminoff
Copy link

skuzminoff commented Jun 9, 2022

Prerequisites:

  • .NET Core 3.1
  • Simple Injector 4.8

I need to replace some of the services registered via Simple Injector for test purposes. It could be easily done using net core built-in DI container. But i found it impossible to implement with Simple Injector.

Default extension points in WebApplicationFactory for doing this are either .ConfigureTestServices() or .ConfigureTestContainer().

The latter is not an option considering this issue dotnet/aspnetcore#14907

But I couldn't do it via .ConfigureTestServices() either.

I can not get Container instance from IServiceCollection (it's simply not there during the .ConfigureTestServices()).

    public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestContainer<Container>(container =>
            {
                //useless, it's impossible to get here
            });

            builder.ConfigureTestServices(services =>
            {
                var container =
                    (Container)services
                    // System.InvalidOperationException: Sequence contains no matching element
                    .Last(d => d.ServiceType == typeof(Container)) 
                    .ImplementationInstance;

                container.Options.AllowOverridingRegistrations = true;
                var testServiceMock = new Mock<ITestService>();
                container.Register(() => testServiceMock.Object);

            });
        }
    }

Even if I hack the actual Container Instance from Startup.cs and try to register service mock, I got "Container is locked" error.

So, I'd like to know, if there is a way to use Simple Injector in such cases and, if positive, how do I do that.

@dotnetjunkie
Copy link
Collaborator

I tried your code, but it actually works on my machine. Could it be that you didn't call services.AddSimpleInjector from inside your Startup.ConfigureServices method? This method adds the Container to the IServiceCollection.

@skuzminoff
Copy link
Author

skuzminoff commented Jun 9, 2022

I tried to follow documentation https://docs.simpleinjector.org/en/latest/aspnetintegration.html

But anyway here is configuration code from the Startup.cs:

public class Startup
{
    //....
    private readonly Container _container = new Container();

    //...

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(...);
        //...

        services.AddSimpleInjector(_container, opts =>
        {
            opts.AddAspNetCore()
                .AddControllerActivation();

            opts.AddLogging();
        });

        SimpleInjectorConfig.Configure(...);

        ///.....
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseSimpleInjector(_container);
        
        // ....

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });

        // ....

        _container.Verify(VerificationOption.VerifyAndDiagnose);
    }
}

@dotnetjunkie
Copy link
Collaborator

Try this:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton(new SimpleInjector.Container());
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    }
}

This adds the container to the collection. If you can't retrieve that instance in your test, there is likely something happening that your code doesn't show. As I said, I tried to reproduce the issue, but a call to .Last(d => d.ServiceType == typeof(Container)) results in a returned registration.

@skuzminoff
Copy link
Author

Thank you, @dotnetjunkie, your snippet helped.

Btw, I've looked through the sources and haven't found the exact place where SimpleInjector.Container is being added to the IServiceCollection. Can you, please, point me to the place?

@skuzminoff
Copy link
Author

@dotnetjunkie
Interesting. There is no adding container to IServiceCollection at version 4.8.
https://github.com/simpleinjector/SimpleInjector/blob/7c8b3dc653c8115291da8f20272f148eafbdfe15/src/SimpleInjector.Integration.ServiceCollection/SimpleInjectorServiceCollectionExtensions.cs#L65

Probably, somewhere else or this issue has been fixed in a latter version.

Anyway, thanks for help, i really appreciate it.

@dotnetjunkie
Copy link
Collaborator

Ah, that very well could be true. Initially, the container wasn't added. I'm unsure whoch version it was added.

But this means that, as long as you're on 4.8, its best to manually add the container to rhe service collection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants