Integration Testing and overriding registered services

We are taking the approach of integration testing by running our .NetCore service in Kestrel.
Which has more benefits to us than integration testing at the AppHost level.

The only issue that we now have is that we want to swap out various dependencies like our persistence store from CosmosDB to an InMemory store, and stub some other things out.

To do that, we simply need to overwrite a couple of the registered services in the running AppHost.Container.

Given the following code, is there a better option (or syntax) than what we are doing here?
(I feel that this code is violating the principle of not mutating the container after it has been built. It would be better to get into the pipeline sometime after the AppHost.Configure method has been called instead)

    [TestClass, TestCategory("Integration")]
    public class CarsApiSpec
    {
        private const string ServiceUrl = "http://localhost:2000/";
        private static IWebHost webHost;
        private static IStorage<CarEntity> storage;

        [ClassInitialize]
        public static void InitializeAllTests(TestContext context)
        {
            webHost = WebHost.CreateDefaultBuilder(null)
                .UseModularStartup<CarsApi.Startup>()
                .UseUrls(ServiceUrl)
                .UseKestrel()
                .ConfigureLogging((ctx, builder) => builder.AddConsole())
                .Build();
            webHost.Start();

            // HACK: Overwrite configured services used only in integration testing
            var container = HostContext.Container;
            container.AddSingleton<IOtherService, StubOtherService>();
            container.AddSingleton<ICarStorage>(c =>
                new CarStorage(c.Resolve<IStorage<CarEntity>>()));
            storage = CarEntityInMemStorage.Create(container.Resolve<ILogger>(), container.Resolve<IDomainFactory>());
            container.AddSingleton(storage);
        }

        [ClassCleanup]
        public static void CleanupAllTests()
        {
            webHost?.StopAsync().GetAwaiter().GetResult();
        }

Can’t you register the test dependencies you want to use in a test AppHost?

I’d personally use a test AppHost with only the required dependencies used by the tests. If you’re going to do any modifications the place to do them is within Configure().

Yes, I understand.
No arguments here that this an approach to certain kinds of integration testing. (I see you strongly recommend it all over the forums, and it certainly has its merits, covering most scenarios).

However, we want some, shall we call them, “smoke tests” that confirm that the real AppHost we have has all its dependencies correctly wired-up, and working end to end, hence why we are using this technique.

I’d still only mutate it within Configure, e.g using an AfterInitCallback, calling a custom static delegate on AppHost if initialized, etc. But since it’s a test you can continue to do whatever works, i.e. instead of properly.

Thanks. Do you know how I would call the AfterInitCallback in the chain of calls to WebHost?

webHost = WebHost.CreateDefaultBuilder(null)
                .UseModularStartup<CarsApi.Startup>()
                .UseUrls(ServiceUrl)
                .UseKestrel()
                .ConfigureLogging((ctx, builder) => builder.AddConsole())
                .Build();
            webHost.Start();

Has to be added before the AppHost is initialized e.g. when registering the AppHost:

app.UseServiceStack(new AppHost
{
    AfterInitCallbacks = {
        host => host.GetContainer().AddSingleton(...)
    }
});

Or you can initialize a static delegate and call it within your AppHost, e.g:

public static Action<Container> OnConfigure { get; set; }

public override void Configure(Container container)
{
    OnConfigure?.Invoke(container);
}