Compose Configuration From an In-Memory Collection¶
Problem¶
Your code under test depends on IConfiguration directly (GetSection, GetValue<T>, configuration-binding helpers) and the test wants a real, predictable IConfiguration instance — not a hand-rolled fake, and no external config file on disk.
Solution¶
using Microsoft.Extensions.Configuration;
IConfiguration BuildConfiguration() =>
new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Database:ConnectionString"] = "test-connection-string",
["Database:TimeoutSeconds"] = "5",
})
.Build();
public sealed class DatabaseConfigurationProfile : ICompositionProfile
{
public void Configure(CompositionBuilder builder) =>
builder.Register<IConfiguration>(BuildConfiguration);
}
[Theory]
[Compose<DatabaseConfigurationProfile>]
public void ReadsTheConnectionStringFromConfiguration(IConfiguration configuration)
{
configuration["Database:ConnectionString"].Should().Be("test-connection-string");
}
Or, wired directly through a hand-built Composer without a test-framework attribute at all:
var composer = Composer.Create(builder => builder
.Register<IConfiguration>(BuildConfiguration));
var repository = composer.Create<OrderRepository>();
Discussion¶
This is ordinary Microsoft.Extensions.Configuration composition — Compono adds nothing beyond the standard Register<T> call. AddInMemoryCollection accepts colon-separated keys ("Database:TimeoutSeconds") the same way a real appsettings.json's nested sections flatten when bound, so configuration.GetSection("Database").GetValue<int>("TimeoutSeconds") and configuration-binding (configuration.GetSection("Database").Get<DatabaseOptions>()) both work exactly as they would against a real configuration source.
There is no Compono.Configuration package — plain ConfigurationBuilder/Register<IConfiguration> composition, as above, is the whole answer for IConfiguration itself. If your code under test instead depends on IOptions<T>/IOptionsSnapshot<T>/IOptionsMonitor<T> for a strongly-typed settings class, that's Compono.Options's job, not this recipe's — the two compose independently and don't need to agree with each other's values.
See also¶
- Layer Configuration Overrides in a Test
- Reuse Configuration Through a Profile
Compono.OptionsPackage Guide — forIOptions<T>/IOptionsSnapshot<T>/IOptionsMonitor<T>instead of plainIConfiguration.