TestFramework.Config
0.4.0
dotnet add package TestFramework.Config --version 0.4.0
NuGet\Install-Package TestFramework.Config -Version 0.4.0
<PackageReference Include="TestFramework.Config" Version="0.4.0" />
<PackageVersion Include="TestFramework.Config" Version="0.4.0" />
<PackageReference Include="TestFramework.Config" />
paket add TestFramework.Config --version 0.4.0
#r "nuget: TestFramework.Config, 0.4.0"
#:package TestFramework.Config@0.4.0
#addin nuget:?package=TestFramework.Config&version=0.4.0
#tool nuget:?package=TestFramework.Config&version=0.4.0
TestFramework.Config
TestFramework.Config provides a simple, composable way to prepare
IServiceProvider and IConfiguration for timeline runs.
Use it when your integration tests need environment-specific configuration, service registration, or per-test overrides.
Install
dotnet add package TestFramework.Config
Quick Start
using Microsoft.Extensions.DependencyInjection;
using TestFramework.Config;
// The provider owns every singleton it creates, so you own the provider: dispose it.
using ServiceProvider serviceProvider = ConfigInstance
.FromJsonFile("appsettings.test.json")
.OverrideConfig("FeatureFlags:UseMockService", "true")
.AddService((services, configuration) =>
{
services.AddHttpClient();
services.AddSingleton<IMyDependency, MyDependency>();
})
.BuildServiceProvider();
Which Configuration Abstraction Do I Use?
For ordinary timeline tests, treat ConfigInstance as the default and primary entry point.
- Use
ConfigInstancewhen you want to prepare configuration, register services, build anIServiceProvider, and pass that provider intoSetupRun(...). - Treat package-specific typed stores such as Azure's
ConfigStore<T>as advanced runtime services that may live inside that provider. They are not a second root setup model for most consumers.
The practical ownership rule is:
ConfigInstanceowns the run setup container.- typed stores live inside that container when a module needs named resource records at runtime.
| If your question is... | Prefer |
|---|---|
"How do I prepare config and services for SetupRun(...)?" |
ConfigInstance |
"How does Azure/SQL/Container look up a named runtime record like MainDb?" |
module-owned ConfigStore<T> inside DI |
| "Do I need to choose one model for the whole test?" | no, start with ConfigInstance; typed stores are advanced runtime services, not a second root setup style |
If you need both, you still start with ConfigInstance, then let your module register or resolve the typed store through DI.
Decision Guide
- You have JSON files, a few overrides, or normal service registration: use
ConfigInstance. - You want a reusable shared base with per-test variants: build a
ConfigInstance, then callSetupSubInstance(). - A richer module such as Azure or SQL needs named resource records like
MainSqlorDefault: keep usingConfigInstancefor setup, and treat the typed store as an implementation detail consumed from DI.
Typical Pattern
Use a shared base config and derive per-test variants:
using TestFramework.Config;
ConfigInstance shared = ConfigInstance
.FromJsonFile("appsettings.test.json")
.Build();
var providerA = shared
.SetupSubInstance()
.OverrideConfig("Run:Tenant", "A")
.BuildServiceProvider();
var providerB = shared
.SetupSubInstance()
.OverrideConfig("Run:Tenant", "B")
.BuildServiceProvider();
Override precedence is last-write-wins within the active builder. A sub-instance starts with the parent instance's merged values and registrations, then applies its own overrides and additions on top.
What a sub-instance inherits, and what it can change:
Init new ConfigInstance (BaseConfig, BaseServices)
| \- BaseServices: from another ConfigInstance, or empty
\- BaseConfig: from JSON, from another ConfigInstance, or empty
Configure the ConfigInstance \
|- Override configs
\- Add services
Create SubConfigInstance (BaseConfig, BaseServices)
| \- BaseServices: inherited from the parent instance
\- BaseConfig: inherited from the parent instance
Each BuildServiceProvider() on a sub-instance produces an independent provider, which is what keeps
one test's service state out of another's.
Integration With Timeline Runs
using TestFramework.Config;
using TestFramework.Core.Timelines;
private static readonly Timeline _timeline = Timeline.Create().Build();
ConfigInstance config = ConfigInstance
.Create()
.Build();
TimelineRun run = await _timeline.SetupRun(config).RunAsync();
run.EnsureRanToCompletion();
SetupRun(ConfigInstance) builds the run's service provider and hands it to the run, which disposes it when
it ends. Pass a provider you built yourself to SetupRun(IServiceProvider) only when you mean to keep and
dispose it.
What A Step Reads: Configured<T>
A package that owns a kind of resource registers a ConfigShape<TConfig> for it - usually from its own
Load…Config() extension through AddConfigShape<TShape, TConfig>(). The shape says which section holds
the entries, how to read one, and which values the run publishes for it. Registering the same shape twice is
a no-op; a second, different shape for the same record is refused.
Inside a step, context.Configured<TConfig>(identifier) returns the resource's configuration as this run
knows it - read from configuration, or published by an environment that started the resource. Both come
from using TestFramework.Config;.
API Summary
ConfigInstance.FromJsonFile(path): start with JSON-backed configurationConfigInstance.Create(): start from an empty configuration/service stateOverrideConfig(...): replace/add config valuesAddService(...): register dependenciesBuild(): materialize a reusableConfigInstanceSetupRun(ConfigInstance): start a run that builds, owns and disposes its service providerAddConfigShape<TShape, TConfig>(): register how a package reads one kind of resourcecontext.Configured<TConfig>(identifier): read a resource's configuration inside a stepBuildServiceProvider(): build aServiceProviderforSetupRun(IServiceProvider). The caller owns the returned provider and must dispose it — it holds every singleton it created. The return type is the concreteServiceProvider, notIServiceProvider, so the compiler can point that out.
Error Contract
ConfigInstance.FromJsonFile(path)throwsFileNotFoundExceptionwhen the file does not exist.ConfigInstance.FromJsonFile(path)throwsInvalidDataExceptionwhen the JSON content cannot be parsed.- Service-registration delegates added through
AddService(...)run duringBuildServiceProvider()and any exception they throw is propagated to the caller.
Advanced Usage Notes
- Prefer
Build()when you want a reusable base configuration that can spawn multiple sub-instances. - Prefer
SetupSubInstance()when tests share most configuration but need a few targeted overrides. - Use the
AddService((services, configuration) => ...)overload when service registration depends on effective configuration values after overrides have been applied. - If a module also uses a typed registry inside DI, keep the ownership boundary clear:
ConfigInstanceprepares the provider, the typed registry serves that module's runtime lookup needs.
Target Frameworks
- .NET 8 (
net8.0) - .NET 10 (
net10.0)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration (>= 10.0.0)
- Microsoft.Extensions.Configuration.Json (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.0)
- Newtonsoft.Json (>= 13.0.4)
- TestFramework.Core (>= 0.7.0)
-
net8.0
- Microsoft.Extensions.Configuration (>= 10.0.0)
- Microsoft.Extensions.Configuration.Json (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.0)
- Newtonsoft.Json (>= 13.0.4)
- TestFramework.Core (>= 0.7.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on TestFramework.Config:
| Package | Downloads |
|---|---|
|
TestFramework.Container.Azure
Docker-backed environment providers for TestFramework timelines for Azure components. |
|
|
TestFramework.UI.Browser
Drive a local browser from inside a TestFramework timeline. Interactions read as imperative steps (Navigate, Click, Fill, Expect) and address elements the way a person does - by role, label, placeholder or visible text - so a renamed or relocated control does not fail a test that only wanted to press it. Every non-exact match is recorded and assertable, ambiguity fails with the candidates and the dial that resolves it, and expected page structure is declared as a typed, reusable field. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|
BREAKING - needs TestFramework.Core 0.7.0; add using TestFramework.Config where a step calls context.Configured<T>, and rebuild, because SetupRun(ConfigInstance) gained caller-info parameters. BREAKING - a custom ConfigShape implements Read(values, identifier) and Values(config, identifier), and CreateAsync always disposes the provider it builds. SetupRun(ConfigInstance) now disposes its provider when the run ends, AddConfigShape refuses a second, different shape for a record, and configuration identifiers match exactly, case included.