dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.37k stars 9.99k forks source link

Migrate Asp.Net Core 2.2 Integration Tests to 3.0 - The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available. #14424

Closed aherrick closed 5 years ago

aherrick commented 5 years ago

We had basic Api integration tests setup as following. When trying to update to 3.0 I get the following error.

System.InvalidOperationException : The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.

How to convert to support 3.0?

public class MainAPITests : BaseAPITests
{
    public MainAPITests(WebApplicationFactory<Startup> factory) : base(factory)
    {
    }

    // tests here
}

public class BaseAPITests : IClassFixture<WebApplicationFactory<Startup>>
{
    public HttpClient Client { get; }
    public TestServer Server { get; }

    public BaseAPITests(WebApplicationFactory<Startup> factory)
    {
        // ensure we use "Test" environment
        factory = factory.WithWebHostBuilder(builder => builder.UseStartup<Startup>().UseEnvironment("Test"));

        Client = factory.CreateDefaultClient();
        Server = factory.Server;

        // get context from startup where we check if test or not and use in memory
        var catalogDB = Server.Host.Services.GetService<CatalogDbContext>();

        // handle additional details
    }
}
javiercn commented 5 years ago

@aherrick Thanks for contacting us.

Instead of doing factory.Server.Services you can simply do factory.Services. The error you are seeing is likely due to the move to generic host. The Services property will do the right thing.

If that doesn't work, let us know.

aherrick commented 5 years ago

Thanks for your help! That got me over that hurdle.

Now I'm confused in a similar way with UI Tests.

I have updated my Web MVC project to have a Program such as this:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

I have Selenium style tests that are setup very similar to this:

https://www.hanselman.com/blog/RealBrowserIntegrationTestingWithSeleniumStandaloneChromeAndASPNETCore21.aspx

What I'm seeing is the protected override TestServer CreateServer(IWebHostBuilder builder) so I'm having hard time understanding what the migration might look like:

I've tried the following (slimmed down) but it doesn't get it. Thoughts on what I'm missing?

    public class SeleniumServerFactory<TStartup> : WebApplicationFactory<Startup>
        where TStartup : class
    {
        public IHost HostWeb { get; set; }

        public SeleniumServerFactory()
        {
            // build up selenium bits here
        }

                // never called but this is where i was previously building up the server
                //
        protected override TestServer CreateServer(IWebHostBuilder builder)
        { 
            return base.CreateServer(builder);
        }

        protected override IHost CreateHost(IHostBuilder builder)
        {
            HostWeb = builder.Build();

            HostWeb.Start();

            return HostWeb;
        }

        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureKestrel(options =>
                {
                    options.Listen(IPAddress.Loopback, TestConstants.Port, listenOptions =>
                    {
                        listenOptions.UseHttps("pfx", "pass");
                    });
                })
                .UseUrls(TestConstants.RootUrl)
                .UseEnvironment("Test");
        }   
    }
bluebaroncanada commented 5 years ago

This thread led me down a rabbit hole for 4 days. This causes an issue where the resolver cannot resolve the appropriate service. The only way I could fix it is to comment out the actual registration in Startup.cs.

System.InvalidOperationException : Cannot resolve scoped service 'ApplicationDbContext' from root provider.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.Microsoft.Extensions.DependencyInjection.ServiceLookup.IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at xxxxxxxxTest..ctor(CustomWebApplicationFactory`1 factory) in C:\Users\xxxxxxx\source\repos\Claim\xxxxxxxxxxTest.cs:line 21