aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

`HttpClient.PostAsync` fails to reach controller endpoint during both Debug and Run sessions in integration tests.

Open khteh opened this issue 8 months ago • 39 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Describe the bug

HttpClient.PostAync does not reach controller endpoint at all. GetAsync and DeleteAsync are OK. The application web UI and swagger UI work perfectly. It seems that HttpClient doesn't manage to reach the controller endpoint somehow. Same behaviour observed in running a debug session of the application and sending a POST request from Postman. The POST request from Postman seems like taking forever and never hit the controller endpoint breakpoint. Debug console log:

[06/06/2025 10:57:19 +08:00] Debug { Id: 1, Name: "AuthenticationFailed" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Failed to authenticate HTTPS connection. {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware", ConnectionId="0HND4KKA9F3V6", ThreadId=18, ExceptionDetail={["Type"]="System.IO.IOException", ["HResult"]=-2146232800, ["Message"]="Received an unexpected EOF or 0 bytes from the transport stream.", ["Source"]="System.Net.Security", ["TargetSite"]="Void MoveNext()"}} 
System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.ReceiveHandshakeFrameAsync[TIOAdapter](CancellationToken cancellationToken)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware.OnConnectionAsync(ConnectionContext context)

[06/06/2025 10:57:19 +08:00] Debug { Id: 2, Name: "ConnectionStop" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection id ""0HND4KKA9F3V6"" stopped. {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Connections", ThreadId=18} 

[06/06/2025 10:57:19 +08:00] Debug { Id: 7, Name: "ConnectionWriteFin" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection id ""0HND4KKA9F3V6"" sending FIN because: ""The Socket transport's send loop completed gracefully."" {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets", ThreadId=18} 

[06/06/2025 10:57:19 +08:00] Debug { Id: 3, Name: "HttpsConnectionEstablished" } IP: CorrelationId: Agent: ContentType: ContentLength: RequestBody: RequestQuery: MemoryUsage:/398065664 Connection "0HND4KKA9F3V7" established using the following protocol: "Tls13" {SourceContext="Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware", ThreadId=8} 

Exception thrown: 'System.InvalidOperationException' in Microsoft.AspNetCore.Server.Kestrel.Core.dll
4
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll

Irconically, once I cancel the request on the Postman side, it hits the contoller POST method/endpoint and the request completes successfully without seeing anything unusual at the Debug Console. Of course, the Postman won't show any response since the request was already cancelled.

Program.cs:

builder.Services.AddControllersWithViews();
app.MapControllers();

Controller:

[Route("api/[controller]")]
[ApiController]
public class AccountsController : ControllerBase { 
    [HttpPost("register")]
    [Consumes("application/json")]
    public async Task<ActionResult> Register([FromBody] Models.Request.RegisterUserRequest request)
    {
        if (!ModelState.IsValid)
        {
            _logger.LogError($"{nameof(Register)} invalid model state!");
            return BadRequest(ModelState);
        }
        _logger.LogDebug($"{nameof(Register)}");
        RegisterUserResponse response = await _mediator.Send(new RegisterUserCommand(request.FirstName, request.LastName, request.Email, request.UserName, request.Password));
        _logger.LogDebug($"{nameof(Register)} response: {JsonSerializer.Serialize(response)}");
        return _mapper.Map<JsonContentResult>(response);
    }
 }

Controller integration test code:

[Collection("Controller Test Collection")]
public class AccountsControllerIntegrationTests
{
    private readonly HttpClient _client;
    private readonly ITestOutputHelper _output;
    public AccountsControllerIntegrationTests(ITestOutputHelper output, CustomWebApplicationFactory<Program> factory) => (_output, _client) = (output, factory.Client);
    [Fact]
    public async Task CanRegisterUserWithValidAccountDetails()
    {
        var httpResponse = await _client.PostAsync("/api/accounts/register", new StringContent(System.Text.Json.JsonSerializer.Serialize(new Models.Request.RegisterUserRequest("John", "Doe", "[email protected]", "johndoe", "Pa$$word1")), Encoding.UTF8, "application/json"));
       ...
   }

CustomWebApplicationFactory:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>, IAsyncLifetime where TStartup : class
{
    public HttpClient Client { get; set; }
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseEnvironment("IntegrationTests");
        builder.ConfigureLogging((p) => p.SetMinimumLevel(LogLevel.Debug));
        builder.ConfigureServices((context, services) =>
        {
            // Create a new service provider.
            services.Configure<GrpcConfig>(context.Configuration.GetSection(nameof(GrpcConfig)));
            services.AddScoped<SignInManager<AppUser>>();
        });
    }
    public async ValueTask InitializeAsync()
    {
        Client = CreateClient(new WebApplicationFactoryClientOptions
        {
            BaseAddress = new Uri("https://localhost:4433")
        });
    }

Expected Behavior

What do you think?

Steps To Reproduce

No response

Exceptions (if any)

Message: 
  System.Threading.Tasks.TaskCanceledException : The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
  ---- System.TimeoutException : Flush was canceled on underlying PipeWriter.
  -------- System.OperationCanceledException : Flush was canceled on underlying PipeWriter.

Stack Trace: 
  HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
  HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
  AccountsControllerIntegrationTests.CanChangePasswordWithValidAccountDetails() line 133
  --- End of stack trace from previous location ---
  ----- Inner Stack Trace -----
  ----- Inner Stack Trace -----
  ThrowHelper.ThrowOperationCanceledException_FlushCanceled()
  PipeWriter.CopyFromAsync(Stream source, CancellationToken cancellationToken)
  <<SendAsync>b__0>d.MoveNext()
  --- End of stack trace from previous location ---
  <<SendAsync>g__RunRequestAsync|0>d.MoveNext()
  --- End of stack trace from previous location ---
  ClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  CookieContainerHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)

.NET Version

10.0.0

Anything else?

.NET SDK:
 Version:           10.0.100
 Commit:            b0f34d51fc
 Workload version:  10.0.100-manifests.5fb86115
 MSBuild version:   18.0.2+b0f34d51f

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.26200
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\10.0.100\

.NET workloads installed:
 [maui-windows]
   Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
   Manifest Version:    10.0.0/10.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maui\10.0.0\WorkloadManifest.json
   Install Type:              Msi

 [maccatalyst]
   Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
   Manifest Version:    26.1.10494/10.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maccatalyst\26.1.10494\WorkloadManifest.json
   Install Type:              Msi

 [android]
   Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
   Manifest Version:    36.1.2/10.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.android\36.1.2\WorkloadManifest.json
   Install Type:              Msi

 [ios]
   Installation Source: VS 18.3.11218.70, VS 17.14.36717.8
   Manifest Version:    26.1.10494/10.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.ios\26.1.10494\WorkloadManifest.json
   Install Type:              Msi

Configured to use workload sets when installing new manifests.
No workload sets are installed. Run "dotnet workload restore" to install a workload set.

Host:
  Version:      10.0.0
  Architecture: x64
  Commit:       b0f34d51fc

.NET SDKs installed:
  6.0.202 [C:\Program Files\dotnet\sdk]
  6.0.401 [C:\Program Files\dotnet\sdk]
  6.0.428 [C:\Program Files\dotnet\sdk]
  7.0.120 [C:\Program Files\dotnet\sdk]
  9.0.112 [C:\Program Files\dotnet\sdk]
  9.0.308 [C:\Program Files\dotnet\sdk]
  10.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 9.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 10.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

khteh avatar May 09 '25 03:05 khteh

Looking at the code provided, I would say that the exception is really just saying "the system under test didn't reply in under 100s, so things cancelled".

As there's no code for that system under test provided, I can't give any insight into why that might be the case.

The test setup seems reasonable, though I would change two things:

  1. I would use the first class APIs to set the environment, not globally set the environment variable for the whole test process.
  2. I would just register logging rather than a specific logger for a specific class - then the framework will just handle that for you automatically.

martincostello avatar May 09 '25 04:05 martincostello

  • I would use the first class APIs to set the environment, not globally set the environment variable for the whole test process.

Are you referring to Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTests");? What do you mean "first class APIs"? Is it something new in ASP.Net Core 9? Never heard of it. Bear it mind that the code has to run on both Windows 11 (VS2022) and Ubuntu (VSCode).

  • I would just register logging rather than a specific logger for a specific class - then the framework will just handle that for you automatically.

Not sure what you mean. Code here: https://github.com/khteh/AspNetCoreWebApi/blob/master/test/Web.Api.IntegrationTests/CustomWebApplicationFactory.cs

khteh avatar May 09 '25 05:05 khteh

By first class APIs I mean the UseEnvironment(string) method, which IIRC, was added in .NET Core 3.0.

Here's an example of its use: example. This code works on any operating system.

For logging I mean if the service provider has logging configured already, then you don't need to explicitly register a service for ILogger<T>.

martincostello avatar May 09 '25 05:05 martincostello

I had a quick look at the link to the repo you provided, and there's a lot of code there to pick through to try and see an issue.

I would suggest as a starting point wiring up the logging to output to xunit (I maintain a project to do exactly that) as you already have references to ITestOutputHelper in your test anyway, and then use that to see if anything useful is output that helps you diagnose what's taking so long. Here's an example of using it (in a whole repo dedicated to showing how to use WebApplicationFactory<T> to test an app): example.

martincostello avatar May 09 '25 05:05 martincostello

By first class APIs I mean the UseEnvironment(string) method, which IIRC, was added in .NET Core 3.0.

Here's an example of its use: example. This code works on any operating system.

This only provides 3 hard-coded options https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.environments?view=net-9.0-pp How does that compare with launchSettings.json https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-9.0#development-and-launchsettingsjson?

khteh avatar May 09 '25 05:05 khteh

I would suggest as a starting point wiring up the logging to output to xunit (I maintain a project to do exactly that) as you already have references to ITestOutputHelper in your test anyway, and then use that to see if anything useful is output that helps you diagnose what's taking so long. Here's an example of using it (in a whole repo dedicated to showing how to use WebApplicationFactory<T> to test an app): example.

There are many build errors when integrating the nuget package to my CustomWebApplicationFactory<TStartup> class. Among them are the conflict IAsyncLifetime has with xunit.core and xunit.v3.core. And the this doesn't work with the template class parameter TStartup.

khteh avatar May 09 '25 05:05 khteh

This only provides 3 hard-coded options

The class does yes, but the documentation I linked you to shows it takes a string, so you can pass it whatever you like and not use the referenced separate class of constants.

martincostello avatar May 09 '25 05:05 martincostello

There are many build errors when integrating the nuget package to my CustomWebApplicationFactory<TStartup> class. Among them are the conflict IAsyncLifetime has with xunit.core and xunit.v3.core. And the this doesn't work with the template class parameter TStartup.

If you read the documentation you'll see there are two packages. Use the right one for the version of xunit you are using.

The documentation also shows other ways to configure the logging than the one in the specific usage I linked you to.

martincostello avatar May 09 '25 05:05 martincostello

For logging I mean if the service provider has logging configured already, then you don't need to explicitly register a service for ILogger<T>.

            services.AddLogging((loggingBuilder) => loggingBuilder
                .SetMinimumLevel(LogLevel.Debug)
                .AddConsole());

How can I check / verify if the service already has logging configured in the WebApplicationFactory subclass's ConfigureWebHost method?Even when it does already have one, I just want a simple logging to the console instead of the complex logging system used by the full-blown web application using Serilog.

khteh avatar May 09 '25 05:05 khteh

AddLogging() adds logging, so ILogger<T> will be registered by that.

Xunit captures the console output by default, so you won't see any console output unless you specifically configure xunit itself to do that. That's why I recommended using the xunit logging package, which logs to the xunit output which is only emitted when a test fails.

martincostello avatar May 09 '25 05:05 martincostello

By first class APIs I mean the UseEnvironment(string) method, which IIRC, was added in .NET Core 3.0.

Here's an example of its use: example. This code works on any operating system.

This doesn't work. string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); in Program.cs reads a null string. I defined the following profile in launchSettings.json but to no avail!

    "Web.Api.IntegrationTests": {
      "commandName": "Project",
      "launchBrowser": true,
        "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "IntegrationTests"
        },
      "applicationUrl": "https://localhost:4433;http://localhost:5000"
    }

khteh avatar May 09 '25 06:05 khteh

The method doesn't set an environment variable value, it configures the host builder with what environment to use. If you need to read it back, use the host environment in the system under test.

martincostello avatar May 09 '25 06:05 martincostello

For logging I mean if the service provider has logging configured already, then you don't need to explicitly register a service for ILogger<T>. AddLogging() adds logging, so ILogger<T> will be registered by that.

AddLogging() is in CustomWebApplicationFactory.ConfigureWebHost(). But you mentioned before that "if the service provider has logging configured already". How do I know / verify that? Is AddLogging in CustomWebApplicationFactory.ConfigureWebHost() something redundant or overriding the existing logging service that I shouldn't do here?

khteh avatar May 09 '25 06:05 khteh

I now get different errors. Some of the intergration tests pass and fail sometimes and they fail with the following error:

  Message: 
    Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' threw in InitializeAsync
    ---- System.InvalidOperationException : The entry point exited without ever building an IHost.

  Stack Trace: 
    ----- Inner Stack Trace -----
    HostingListener.CreateHost()
    <>c__DisplayClass10_0.<ResolveHostFactory>b__0(String[] args)
    DeferredHostBuilder.Build()
    WebApplicationFactory`1.CreateHost(IHostBuilder builder)
    WebApplicationFactory`1.ConfigureHostBuilder(IHostBuilder hostBuilder)
    WebApplicationFactory`1.EnsureServer()
    WebApplicationFactory`1.get_Services()
    CustomWebApplicationFactory`1.InitializeAsync() line 37

khteh avatar May 09 '25 06:05 khteh

That error suggests that the Startup code of the app is throwing an exception.

At this point I think we've established that this isn't an issue with ASP.NET Core itself, but with your usage of it in your application. This is probably better suited to further troubleshooting in a forum like Stack Overflow.

martincostello avatar May 09 '25 07:05 martincostello

AddLogging() is in CustomWebApplicationFactory.ConfigureWebHost(). But you mentioned before that "if the service provider has logging configured already". How do I know / verify that? Is AddLogging in CustomWebApplicationFactory.ConfigureWebHost() something redundant or overriding the existing logging service that I shouldn't do here?

My point is that if AddLogging is called somewhere then you don't need to explicitly register ILogger<T> as per the code snippet in the issue description.

This should be redundant:

    services.AddScoped<ILogger<UserRepository>>(provider =>
            {
                ILoggerFactory loggerFactory = provider.GetRequiredService<ILoggerFactory>();
                return loggerFactory.CreateLogger<UserRepository>();
            });

martincostello avatar May 09 '25 07:05 martincostello

I hit the same error and stack trace in my original post when I run individual tests.

khteh avatar May 09 '25 07:05 khteh

Is this a valid code as it keeps throwing "the entrypoint exited without ever building an IHost" error.

    public async ValueTask InitializeAsync()
    {
        using (var scope = Services.CreateScope())

khteh avatar May 09 '25 08:05 khteh

That error suggests that the Startup code of the app is throwing an exception.

I think it's the same - the code is throwing before the service collection is created (that service collection is a wrapper to the lazily created real one IIRC). Put some breakpoints in your startup code and see how far things get.

martincostello avatar May 09 '25 08:05 martincostello

In the debug session, it all stops at _client.PostAsync and never hits a breakpoint in the controller post method. That means the startup code may have succesfully run completely.

In a run session, I add a line app.Logger.LogInformation("app.Run()..."); before app.Run() in Program.cs and don't see the line printed in Output.

khteh avatar May 09 '25 08:05 khteh

Step through it from the very first line in Program.cs and see if it hits anything it might be throwing before the service collection is built.

If it doesn't hit any of your Program.cs at all, then the problem is somewhere in your test setup.

martincostello avatar May 09 '25 08:05 martincostello

I just said that there is no HttpClient error/exception observed in a debug session. It just never hit the controller endpoint.

How do I see the following messages in Program.cs in a run session?

    Log.Information("app.Run()...");
    app.Run();
}
catch (Exception ex) when (ex.GetType().Name is not "HostAbortedException") // https://github.com/dotnet/runtime/issues/60600
{
    Log.Fatal($"Exception: {ex.Message}");
}

khteh avatar May 09 '25 09:05 khteh

    public async ValueTask InitializeAsync()
    {
        using (var scope = Services.CreateScope())
            try
            {
                var scopedServices = scope.ServiceProvider;
                var appDb = scopedServices.GetRequiredService<AppDbContext>();
                var identityDb = scopedServices.GetRequiredService<AppIdentityDbContext>();
                ILogger logger = scopedServices.GetRequiredService<ILogger>();
                // Ensure the database is created.
                await appDb.Database.EnsureCreatedAsync();
                await identityDb.Database.EnsureCreatedAsync();
                // Seed the database with test data.
                await SeedData.PopulateTestData(identityDb, appDb);
                logger.LogInformation($"{nameof(InitializeAsync)} completes");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{nameof(InitializeAsync)} exception! {ex}");
                throw;
            }
    }
Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' threw in InitializeAsync
---- System.InvalidOperationException : No service for type 'Microsoft.Extensions.Logging.ILogger' has been registered.

khteh avatar May 09 '25 09:05 khteh

Yes, I saw that you said that, but I didn't say anything about the controller.

Put a breakpoint here in Program.cs - does it get hit? If it doesn't, then the tests don't even get as far as trying to start up your app.

martincostello avatar May 09 '25 09:05 martincostello

Every single line runs in Debug session.

khteh avatar May 09 '25 09:05 khteh

Where does Console.WriteLine($"{nameof(InitializeAsync)} completes"); output go in VS2022? I checked "Debug" and "Tests" outputs but don't see anything in there

khteh avatar May 09 '25 09:05 khteh

I have the following in ConfigureWebHost():

            services.AddLogging((loggingBuilder) =>
                loggingBuilder.SetMinimumLevel(LogLevel.Debug).AddXUnit().AddConsole());

khteh avatar May 09 '25 09:05 khteh

If it's not in the Debug window, then it's probably being captured by xunit.

We've reached the limits of how much I can help you with debugging your app now. There should be plenty of examples and documentation in the README/repo for my xunit logging library to help you with getting the test output wired-up.

martincostello avatar May 09 '25 09:05 martincostello

This line of code in README:

.AddLogging((builder) => builder.AddXUnit(outputHelper))

Is ITestOutputHelper available in ConfigureWebHost()? Otherwise how to add that into AddXUnit?

khteh avatar May 09 '25 10:05 khteh

You need to route it through from the test class to wherever you need to use it. Take a look at the sample for inspiration.

martincostello avatar May 09 '25 10:05 martincostello