System.AggregateException: 'One or more hosted services failed to stop. (The operation was canceled.)'
Asked Answered
C

2

7

I am writing integration test using XUnit and my web api code is also in C# NET 6 and EF Core.

When I debug it, it can reach the web api and its service layer. But when it reaches EF Core context query example private Message? GetMessage() => _myContext.Messages.OrderBy(m => m.CreatedUtc).FirstOrDefault();, it breaks at Program.cs.

enter image description here

This is the code for TestingWebAppFactory class

public class TestingWebAppFactory<TEntryPoint> : WebApplicationFactory<Program> where TEntryPoint : Program
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var descriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbContextOptions<MyContext>));

            if (descriptor != null)
                services.Remove(descriptor);

            services.AddDbContext<MyContext>(options =>
            {
                options.UseInMemoryDatabase("myinmemorydb");
            });

            var sp = services.BuildServiceProvider();
            using (var scope = sp.CreateScope())
            using (var appContext = scope.ServiceProvider.GetRequiredService<MyContext>())
            {
                try
                {
                    appContext.Database.EnsureCreated();
                }
                catch (Exception ex)
                {
                    //Log errors or do anything you think it's needed
                    throw;
                }
            }
        });
    }
}

and this is my code in Xunit

public class MyServiceTest : IClassFixture<TestingWebAppFactory<Program>>
{

    private readonly HttpClient _client;

    public MyServiceTest(TestingWebAppFactory<Program> factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task WhenAValidMessagePosted_ThenShouldReturn()
    {
        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        source.CancelAfter(TimeSpan.FromSeconds(5));
        var response = await _client.GetAsync("https://localhost:xxxx/api/service/message/post?cronExpresson=0");
    }
}
Cohabit answered 25/8, 2022 at 3:18 Comment(1)
Any inner exception? Can you manually create a DbContext and access your in-memory database? Can you try to not dispose your appContext in TestingWebAppFactory (see here)Catgut
O
2

I found a similar error, and I implemented the interface IAsyncLifetime:

public class MyServiceTest : IClassFixture<TestingWebAppFactory<Program>>, IAsyncLifetime
{
    private readonly TestingWebAppFactory<Program> _factory;
    private readonly HttpClient _client;

    public MyServiceTest(TestingWebAppFactory<Program> factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
    }

    public Task InitializeAsync()
    {
       using (_waf.CreateDefaultClient()) { }

       return Task.CompletedTask;
    }

    public async Task DisposeAsync()
    {
       await using var scope = _waf.Services.CreateAsyncScope();
    }

    [Fact]
    public async Task WhenAValidMessagePosted_ThenShouldReturn()
    {
        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        source.CancelAfter(TimeSpan.FromSeconds(5));
        var response = await _client.GetAsync("https://localhost:xxxx/api/service/message/post?cronExpresson=0");
    }
}

Based on this.

Orlene answered 11/9, 2023 at 15:14 Comment(0)
U
1

It seems that the problem might be related to how you configure your EF Core context within the testing environment.

Try ensuring that your test project references the necessary NuGet packages for EF Core and EF Core In-Memory Database.

Verify that your MyContext class inherits from DbContext and is correctly configured.

Update the TestingWebAppFactory class to use the correct generic type parameter (TEntryPoint) for the WebApplicationFactory. Try replacing Program with TEntryPoint like this:

public class TestingWebAppFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
    // ...
}

Check that your MyContext class is registered correctly in the testing environment. In your TestingWebAppFactory class, replace Program with TEntryPoint when registering the DbContextOptions<MyContext>. Try updating your ConfigureWebHost method like this:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    builder.ConfigureServices(services =>
    {
        var descriptor = services.SingleOrDefault(
            d => d.ServiceType == typeof(DbContextOptions<MyContext>));

        if (descriptor != null)
            services.Remove(descriptor);

        services.AddDbContext<MyContext>(options =>
        {
            options.UseInMemoryDatabase("myinmemorydb");
        });
        
        // ...
    });
}

Ensure that the MyContext instance is properly resolved in your test class. In your MyServiceTest class, update the TestingWebAppFactory generic type parameter to TEntryPoint like this:

public class MyServiceTest : IClassFixture<TestingWebAppFactory<Program>>
{
    private readonly HttpClient _client;

    public MyServiceTest(TestingWebAppFactory<Program> factory)
    {
        _client = factory.CreateClient();
    }

    // ...
}

Hope this is helpful!

Uchish answered 30/5, 2023 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.