Skip to content

Run tests in parallel using WebApplicationFactory throw exception #577

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)

Activity

  1. zhiyuanliang-ms commented on Jan 5, 2026

    @zhiyuanliang-ms
    Member

    Hey, Vitalii Koretskyi (@wondertalik)

    Thanks for reporting the issue.

    Could you please create a small, minimal repo (or a zip file) that reproduces the issue? It will help us investigate the problem more effectively.
    From the code snippet you provided (except for the error message), I didn't find how you used feature managment lib.
    Thank you for your help!

  2. wondertalik commented on Jan 8, 2026

    @wondertalik
    Author

    Hey, Zhiyuan Liang (@zhiyuanliang-ms)

    From the code snippet you provided (except for the error message), I didn't find how you used feature managment lib.
    Thank you for your help!

    I use in a very straight way.

    let addFeatureManagements (services: IServiceCollection) : IServiceCollection =
        services.AddFeatureManagement() |> ignore
    
        services
    internal class MigrationService(
        IServiceProvider serviceProvider,
        IFeatureManager featureManager,
        ILogger<MigrationService> logger)
        : IMigrationService
    {
        private readonly List<IMigration> _migrations = [];
    
    ...
    
            foreach (IMigration migration in missingMigrations)
            {
                if (!await featureManager.IsEnabledAsync(migration.Feature))
                {
                    logger.LogInformation("Feature '{Feature}' is not enabled for {Version}", migration.Feature,
                        migration.Version);
                    continue;
                }
      "FeatureManagement": {
        "General": true,
      },

    From the code snippet you provided (except for the error message), I didn't find how you used feature managment lib.
    Thank you for your help!

    Let me try to figured out again. I created a repo as example and don't face the error there. I will keep you updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions