pengweiqhca / Xunit.DependencyInjection

Use Microsoft.Extensions.DependencyInjection to resolve xUnit test cases.
MIT License
370 stars 49 forks source link

MinimalApiHostBuilderFactory not working when Program has Services.AddHttpClient #114

Open vgoranski opened 7 months ago

vgoranski commented 7 months ago

Describe the bug When using MinimalApiHostBuilderFactory.GetHostBuilder<Program>() and Program.cs has builder.Services.AddHttpClient() it seems that the test server is not created and injected HttpClient in the test class has BaseAddress = null

To Reproduce Program.cs

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// The line that causes the bug
builder.Services.AddHttpClient("test");

builder.Services.AddControllers();

var app = builder.Build();

app.Map("/hello", () => "Hello world");
app.MapDefaultControllerRoute();

await app.RunAsync();

public class HomeController
{
    public IActionResult Index() => new ContentResult { Content = "Hello world" };
}

MinimalApiTest.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Xunit.DependencyInjection.AspNetCoreTesting;

namespace Xunit.DependencyInjection.Test.AspNetCore;

public class MinimalApiTest(HttpClient httpClient)
{
    [Fact]
    public async Task MinimalApiRouteResponseTest()
    {
        var responseText = await httpClient.GetStringAsync("/hello");
        Assert.Equal("Hello world", responseText);
    }

    public class Startup
    {
        public IHostBuilder CreateHostBuilder() => MinimalApiHostBuilderFactory.GetHostBuilder<Program>()
            .ConfigureHostConfiguration(builder =>
                builder.AddInMemoryCollection([KeyValuePair.Create(HostDefaults.EnvironmentKey, "Testing")]));
    }
}

Expected behavior builder.Services.AddHttpClient in Program.cs should not prevent the creation of the test server.

WeihanLi commented 7 months ago

The AddHttpClient extension will register a HttpClientservice, so the HttpClient registered would be HttpClient in this case, you can inject IHost host and use host.GetTestClient() to get a test HttpClient

Edit >> add a sample

public class ServiceTest(IHost host)
{
    private readonly HttpClient _testClient = host.GetTestClient();

    [Fact]
    public async Task ApiTest()
    {
        using var response = await _testClient.GetAsync("/");
        response.EnsureSuccessStatusCode();
    }
}
vgoranski commented 7 months ago

The solution with host.GetTestClient() works fine, but it creates a new HttpClient for every test class. I guess it would be better if the HttpClient for the test server is used as a singleton?

WeihanLi commented 7 months ago

I guess it would be better if the HttpClient for the test server is used as a singleton?

I think so, while since the test HttpClient would not create a socket connection or send a real request , it would be cheap to create a new one, so it may be acceptable for tests. Since HttpClient is thread-safe, so I think we could keep it as a singleton

Trying to propose an ITestClientWrapper, does this make sense?

https://github.com/pengweiqhca/Xunit.DependencyInjection/pull/115