using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Abc;
public sealed class KestrelWebAppFactory : WebApplicationFactory
{
protected override IHost CreateHost(IHostBuilder builder)
{
// Build the TestServer host first (required by WebApplicationFactory)
var testHost = builder.Build();
// Reconfigure the builder to use a real Kestrel server on a dynamic port
builder.ConfigureWebHost(webHost =>
{
webHost.UseKestrel();
webHost.UseUrls("http://127.0.0.1:0");
});
// Build & start the Kestrel host
var kestrelHost = builder.Build();
kestrelHost.Start();
// Wait for Kestrel to replace :0 with the actual port
var server = kestrelHost.Services.GetRequiredService();
var addressesFeature = server.Features.Get()!;
// Small spin-wait until Kestrel publishes a concrete port
var sw = System.Diagnostics.Stopwatch.StartNew();
string bound = null;
while (sw.Elapsed < TimeSpan.FromSeconds(5))
{
bound = addressesFeature.Addresses.FirstOrDefault(a => !a.EndsWith(":0", StringComparison.Ordinal));
if (bound is not null) break;
Thread.Sleep(25);
}
if (bound is null)
throw new InvalidOperationException("Kestrel did not publish a bound address.");
// Point the factory’s HttpClient at the real server so Playwright can use it
ClientOptions.BaseAddress = new Uri(bound);
// Start and return the TestServer host (WAF expects this)
testHost.Start();
return testHost;
}
}
Затем у меня есть следующий прибор, который я использую в своем тесте
У меня есть следующий код для запуска сервера Kestrel на случайном порту. [code]using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;
namespace Abc;
public sealed class KestrelWebAppFactory : WebApplicationFactory { protected override IHost CreateHost(IHostBuilder builder) { // Build the TestServer host first (required by WebApplicationFactory) var testHost = builder.Build();
// Reconfigure the builder to use a real Kestrel server on a dynamic port builder.ConfigureWebHost(webHost => { webHost.UseKestrel(); webHost.UseUrls("http://127.0.0.1:0"); });
// Build & start the Kestrel host var kestrelHost = builder.Build(); kestrelHost.Start();
// Wait for Kestrel to replace :0 with the actual port var server = kestrelHost.Services.GetRequiredService(); var addressesFeature = server.Features.Get()!;
// Small spin-wait until Kestrel publishes a concrete port var sw = System.Diagnostics.Stopwatch.StartNew(); string bound = null; while (sw.Elapsed < TimeSpan.FromSeconds(5)) { bound = addressesFeature.Addresses.FirstOrDefault(a => !a.EndsWith(":0", StringComparison.Ordinal)); if (bound is not null) break; Thread.Sleep(25); } if (bound is null) throw new InvalidOperationException("Kestrel did not publish a bound address.");
// Point the factory’s HttpClient at the real server so Playwright can use it ClientOptions.BaseAddress = new Uri(bound);
// Start and return the TestServer host (WAF expects this) testHost.Start(); return testHost; } } [/code] Затем у меня есть следующий прибор, который я использую в своем тесте [code]namespace AbcE2ETests;
// Hosts the Kestrel server and Playwright browser for a test class. public class ServerFixture : IAsyncLifetime { private const string PlaywrightContextStoragePath = "/temp/playwrightstate.json";
public KestrelWebAppFactory Factory { get; private set; }
public Uri BaseUrl => Factory.ClientOptions.BaseAddress; public IPlaywright Playwright { get; private set; } public IBrowser Browser { get; private set; } public IBrowserContext Context { get; private set; } public IPage Page { get; private set; }
public virtual async Task InitializeAsync() { Factory = new KestrelWebAppFactory(); await WaitForServerStartAsync();
// Initialize Playwright and a fresh browser context/page for this fixture await SignInAsync(); }
public string GetFullUrl(string relativeUrl) => new Uri(BaseUrl, relativeUrl).ToString();
private async ValueTask DisposePlaywrightAsync() { try { if (Context is not null) await Context.CloseAsync(); if (Browser is not null) await Browser.CloseAsync(); } finally { Playwright?.Dispose();