testcontainers / testcontainers/testcontainers-dotnet
[Enhancement]: Support `ConfigurationProvider` (ASP.NET integration) together with modules
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 4.4k
- Forks
- 357
- Avg merge
- 14h 42m
- Merged PRs (30d)
- 16
Description
Problem
There are several ways to integrate Testcontainers into ASP.NET (integration) tests. Developers often have to write the code to leverage Testcontainers into ASP.NET (integration) tests repeatedly.
Solution
To simplify the integration of dependent services into ASP.NET applications using Testcontainers, we can utilize Microsoft's IConfigurationSource interface and the ConfigurationProvider class. These allow us to initiate default module configurations and set up the actual ASP.NET application, making it straightforward for developers to incorporate Testcontainers into their ASP.NET integration tests and set up their ASP.NET configuration with the dependent services.
private sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(configure =>
{
configure.Add(new RedisConfigurationSource());
});
}
}
private sealed class RedisConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new RedisConfigurationProvider();
}
}
private sealed class RedisConfigurationProvider : ConfigurationProvider
{
private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public override void Load()
{
// Until the asynchronous configuration provider is available,
// we use the TaskFactory to spin up a new task that handles the work:
// https://github.com/dotnet/runtime/issues/79193
// https://github.com/dotnet/runtime/issues/36018
TaskFactory.StartNew(LoadAsync)
.Unwrap()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}
public async Task LoadAsync()
{
var redisContainer = new RedisBuilder().Build();
await redisContainer.StartAsync()
.ConfigureAwait(false);
Set("ConnectionStrings:RedisCache", redisContainer.GetConnectionString());
}
}
The interesting part here is the LoadAsync() member that starts the dependent service and sets the connection string. The actual app can simply read the connection string as it usually does using Configuration.GetConnectionString("RedisCache").
I am still considering the best place to implement and store the interface and class. Overall, I aim to avoid implementing them in every module and introducing extra dependencies like Microsoft.Extensions.Configuration to the modules.
Benefit
Developers will be able to integrate Testcontainers into tests much more simply. Leveraging it into ASP.NET (integration) tests would only require a single line and makes the startup and teardown implementation obsolete.
builder.ConfigureAppConfiguration(configure => configure.Add(new RedisConfigurationSource()));
Alternatives
-
Would you like to help contributing this enhancement?
Yes
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the module integration patterns and the IConfigurationSource/ConfigurationProvider example in the issue. Determine where shared support can live without adding Microsoft.Extensions.Configuration to every module, then verify that a module can configure an ASP.NET integration test with one line and that dependent-service startup and configuration loading work as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100