microsoft / microsoft/FeatureManagement-Dotnet
Run tests in parallel using WebApplicationFactory throw exception
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 1.2k
- Forks
- 129
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 5
Description
In api project i use Microsoft.FeatureManagement library and it works until i want to run tests in parallel.
to fix the problem i tried to put lock and it works, but it's the same that run then tests sequentially
// Static lock to prevent concurrent host/server initialization across factory instances
// This prevents race conditions in service registration (e.g., AddFeatureManagement)
// while still allowing each test class to have its own factory instance
private static readonly object _hostCreationLock = new object();
/// <summary>
/// Override CreateHost to synchronize host creation across parallel factory instances.
/// This ensures service registration (including AddFeatureManagement) happens sequentially,
/// preventing race conditions while still allowing parallel test execution.
/// </summary>
protected override IHost CreateHost(IHostBuilder builder)
{
lock (_hostCreationLock)
{
return base.CreateHost(builder);
}
}
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
public class CustomWebApplicationFactory : WebApplicationFactory<AssemblyMarker>
{
private HttpMessageHandler? _optiConfigHttpMessageHandler;
public void SetOptiConfigHttpMessageHandler(HttpMessageHandler handler)
{
_optiConfigHttpMessageHandler = handler;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Configure logging for tests
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSimpleConsole(options =>
{
options.SingleLine = true;
options.TimestampFormat = "HH:mm:ss.fff ";
options.UseUtcTimestamp = true;
options.IncludeScopes = false;
});
logging.SetMinimumLevel(LogLevel.Debug);
});
});
}
}
using Api.Private.IntegrationTests.Infrastructure;
using Identity.Tests.Shared.Mocking.ExternalIdentityProvider;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
namespace Api.Private.IntegrationTests.GraphQL.Dashboards.OptiLeads;
public class GetSalesRealisedTests : IClassFixture<CustomWebApplicationFactory>, IDisposable
{
private readonly IServiceScope _scope;
private readonly IPrivateClient _client;
private readonly HttpClient _httpClient;
private readonly ITestContextAccessor _testContextAccessor;
public GetSalesRealisedTests(
CustomWebApplicationFactory factory,
ITestContextAccessor testContextAccessor)
{
_testContextAccessor = testContextAccessor;
// Create a scope from the factory's services
_scope = factory.Services.CreateScope();
// Configure the test HttpClient with the correct BaseAddress
_httpClient = factory.CreateClient();
_httpClient.BaseAddress =
new Uri(_httpClient.BaseAddress ?? new Uri("http://localhost"), "/graphql-private");
factory.Server.PreserveExecutionContext = true;
// Register the TestHttpClientFactory in the scope
var services = new ServiceCollection();
services.AddSingleton<IHttpClientFactory>(_ => new TestHttpClientFactory(_httpClient));
services.AddPrivateClient();
var serviceProvider = services.BuildServiceProvider();
_client = serviceProvider.GetRequiredService<IPrivateClient>();
}
[Fact]
public async Task GetSalesRealised_ReturnsDataForUserWithPermissions()
{
}
public void Dispose()
{
_scope.Dispose();
_httpClient.Dispose();
}
}
at System.Collections.Generic.List`1.Enumerator.MoveNext()
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
at Microsoft.FeatureManagement.FeatureManagementBuilder.AddFeatureFilter[T]()
at Microsoft.FeatureManagement.ServiceCollectionExtensions.GetFeatureManagementBuilder(IServiceCollection services)
at Microsoft.FeatureManagement.ServiceCollectionExtensions.AddFeatureManagement(IServiceCollection services)
at API.CompositionRoot.addFeatureManagements(IServiceCollection services) in /Users/admin/Works/NewOne/backend/src/API/CompositionRoot.fs:line 373
at API.CompositionRoot.compose(IConfiguration cfg, IServiceCollection services) in /Users/admin/Works/NewOne/backend/src/API/CompositionRoot.fs:line 399
at <StartupCode$API>.$Program.main@() in /Users/admin/Works/NewOne/backend/src/API/Program.fs:line 43
at InvokeStub_$Program.main@(Object, Object, IntPtr*)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Contributor guide
No contributing guide indexed for this repository
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 reproducing parallel integration-test execution with CustomWebApplicationFactory and inspect the FeatureManagement stack trace at API.CompositionRoot.fs lines 373 and 399 and API/Program.fs line 43. Compare host creation and service registration across the shown test fixture, then verify that the tests run in parallel without the exception and without requiring sequential locking.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, fsharp
- Domain
- backend, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100