A unit test builds the class it checks by itself: new SlotFinder(calendar). It never asks the container how the app assembles its parts,
and it reads no configuration. But that is exactly where errors arise that show up only in production: a service with the
wrong lifetime, a setting that is named differently in the Bicep template than in the code, a factory that under certain circumstances
returns null. In K2 you wrote a first startup test for this. In this lesson you turn it into a test that starts the Kladde
the way it starts in Azure and resolves every registration of its own.
What the container checks at startup
The .NET container can run two checks when it is built:
ValidateOnBuildgoes through every registration when building and checks whether each service's constructor can be satisfied with the registered services. If a dependency is missing, startup fails instead of the first request.ValidateScopesforbids fetching a service with the Scoped lifetime from the root container or putting it into a singleton. A singleton that holds on to a scoped service otherwise keeps it forever, for example aDbContextwith all the objects it ever loaded.
WebApplication.CreateBuilder and Host.CreateApplicationBuilder turn on both only in the Development environment. The following
program registers a singleton that needs a scoped service and builds the host once as Development and once as Production:
// captured.cs
#:package Microsoft.Extensions.Hosting@10.0.14
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings
{
Args = args,
EnvironmentName = args.FirstOrDefault() ?? Environments.Production,
});
builder.Services.AddScoped<OfficeCalendar>();
builder.Services.AddSingleton<SlotFinder>();
try
{
using var host = builder.Build();
Console.WriteLine($"{builder.Environment.EnvironmentName}: built, the error goes unnoticed.");
}
catch (AggregateException ex)
{
Console.WriteLine($"{builder.Environment.EnvironmentName}: {ex.InnerExceptions[0].Message}");
}
public sealed class OfficeCalendar;
public sealed class SlotFinder(OfficeCalendar calendar)
{
public OfficeCalendar Calendar { get; } = calendar;
}
dotnet run captured.cs -- Development
dotnet run captured.cs -- Production
Development: Error while validating the service descriptor 'ServiceType: SlotFinder Lifetime: Singleton ImplementationType: SlotFinder': Cannot consume scoped service 'OfficeCalendar' from singleton 'SlotFinder'.
Production: built, the error goes unnoticed.
Because the Kladde runs as Production in production and your integration tests should also manage without Development, you turn on
both checks explicitly in the test. Outside Development they are off for the sake of speed:
ValidateOnBuild costs time at startup, ValidateScopes at resolutions.