How to fake declared services in Startup.cs during testing?

The only thing yo need to change is to use ConfigureTestServices instead of ConfigureServices. ConfigureTestServices runs after your Startup, therefor you can override real implementations with mocks/stubs. ConfigureServices was newer intended for that purpose, rather, it configures "host services", which are used during the host-building phase of the application, and copied into the application's DI container.

ConfigureTestServices is available in ASP Core version 2.1 and higher.

var host = new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureTestServices((services) =>
    {
        //Setup injection
        services.AddTransient<IExternalService>((a) =>
        {
            return myExtService.Object;
        });
    });

The only option I know of is to setup WebHostBuilder with UseEnvironment:

var host = new WebHostBuilder()
            .UseStartup<Startup>()
            .ConfigureServices(services =>
            {
                //Setup injection
                services.AddTransient<IExternalService>(provider =>
                {
                    return myExtService.Object;
                });
            })
            .UseEnvironment("IntegrationTest");

And then add a condition in the ConfigureServices method in the Startup:

public void ConfigureServices(IServiceCollection services)
    {
        if (Configuration["Environment"] != "IntegrationTest")
        {
            services.AddTransient<IExternalService, ExternalService>();
        }

        services.AddMvc();

        // ...
    }

UPDATE:

I did some more poking around and another option is to not use UseStartup extension method but rather configure the WebHostBuilder directly. You can do this a number of ways but I thought that you could possibly create your own extension method to create a template in your tests:

public static class WebHostBuilderExt
{
    public static WebHostBuilder ConfigureServicesTest(this WebHostBuilder @this, Action<IServiceCollection> configureServices)
    {
        @this.ConfigureServices(services =>
            {
                configureServices(services);

                services.AddMvc();
            })
            .Configure(builder =>
            {
                builder.UseMvc();
            });
        return @this;
    }
}

Now your tests can be setup like the following:

        var host = new WebHostBuilder()
            .ConfigureServicesTest(services =>
            {
                //Setup injection
                services.AddTransient<IInternalService>(provider =>
                {
                    return myExtService.Object;
                });
            });

        var server = new TestServer(host);

This means that you will have to explicitly setup all the implementations that the container will resolve for the specific endpoint you are calling. You can choose to mock or use the the concrete implementations.