我有一個 .NET 6 應用程式(帶有 ASP.NET Core Web 部件)。我使用cypress進行 E2E 測驗。在我們從 .NET Framework 遷移到 .NET 6 之后,我想在 Web 應用程式上運行 E2E cypress 測驗。
我的程序是我實際上在 C# 中運行 nUnit 測驗來初始化記憶體中的 SQLite 資料庫,然后將此資料庫連接用于我的應用程式。我使用這種方法在運行實際測驗之前創建 E2E 測驗所需的資料。然后我從 C# 單元測驗中運行 cypress。這一切在 .NET Framework 中運行良好,Web 應用程式在測驗期間實際運行,因此它可以在 IIS 中的真實 URL 上使用。
使用 .NET 6,我們有允許在記憶體中運行應用程式的類WebApplicationFactory 。但是,我希望我的 Web 應用程式在具有真實埠的真實 URL 下可用,因此我可以針對它啟動 cypress 測驗。
在探索了這個 GitHub 討論的各種想法之后,我創建了一個真實測驗應用程式工廠(具有真實 IP 和埠):
public abstract class RealTestServerWebAppFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
private bool _disposed;
private IHost _host;
public RealTestServerWebAppFactory()
{
ClientOptions.AllowAutoRedirect = false;
ClientOptions.BaseAddress = new Uri("http://localhost");
}
public string ServerAddress
{
get
{
EnsureServer();
return ClientOptions.BaseAddress.ToString();
}
}
public override IServiceProvider Services
{
get
{
EnsureServer();
return _host!.Services!;
}
}
// Code created with advices from https://github.com/dotnet/aspnetcore/issues/4892
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
// Final port will be dynamically assigned
builder.UseUrls("http://127.0.0.1:0");
builder.ConfigureServices(ConfigureServices);
}
protected override IHost CreateHost(IHostBuilder builder)
{
// Create the host for TestServer now before we
// modify the builder to use Kestrel instead.
var testHost = builder.Build();
// Modify the host builder to use Kestrel instead
// of TestServer so we can listen on a real address.
builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
// Create and start the Kestrel server before the test server,
// otherwise due to the way the deferred host builder works
// for minimal hosting, the server will not get "initialized
// enough" for the address it is listening on to be available.
// See https://github.com/dotnet/aspnetcore/issues/33846.
_host = builder.Build();
_host.Start();
// Extract the selected dynamic port out of the Kestrel server
// and assign it onto the client options for convenience so it
// "just works" as otherwise it'll be the default http://localhost
// URL, which won't route to the Kestrel-hosted HTTP server.
var server = _host.Services.GetRequiredService<IServer>();
var addresses = server.Features.Get<IServerAddressesFeature>();
ClientOptions.BaseAddress = addresses!.Addresses
.Select(x => new Uri(x))
.Last();
// Return the host that uses TestServer, rather than the real one.
// Otherwise the internals will complain about the host's server
// not being an instance of the concrete type TestServer.
// See https://github.com/dotnet/aspnetcore/pull/34702.
testHost.Start();
return testHost;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!_disposed)
{
if (disposing)
{
_host?.Dispose();
}
_disposed = true;
}
}
protected abstract void ConfigureServices(IServiceCollection services);
/// <summary>
/// Makes sure that the server has actually been run.
/// It must be executed at lest once to have the address and port assigned and the app fully working
/// </summary>
private void EnsureServer()
{
if (_host is null)
{
using var _ = CreateDefaultClient();
}
}
}
然后我創建了一個派生自 的類RealTestServerWebAppFactory,我在其中設定了一些測驗依賴注入,但這對于我們的示例來說并不重要。我們稱這個子類MyServerTestApp。
測驗中的實體創建如下所示:
var serverTestApp = new MyServerTestApp();
serverTestApp.ServerAddress;
在第二行,EnsureServer()呼叫為應用程式創建默認客戶端的方法。它還CreateHost(IHostBuilder builder)從RealTestServerWebAppFactory具有所有魔力的類中呼叫。在本地,在此行之后,我有應用程式的 URL,我可以通過 Web 瀏覽器(或 E2E 測驗)訪問它。
現在,問題是測驗在本地運行良好。但是,我也在 CI 上執行它們 - 更準確地說是 TeamCity。TeamCity 用于運行這些測驗的dotnet test命令如下所示:
"C:\Program Files\dotnet\dotnet.exe" test C:\TeamCity\buildAgent\work\c9a5fb5bca7f6666\Tests\E2ETests\bin\Release\net6.0-windows\MyApp.E2ETests.dll /logger:logger://teamcity /TestAdapterPath:C:\TeamCity\buildAgent\plugins\dotnet\tools\vstest15 /logger:console;verbosity=normal
這實際上是對dotnet test命令的直接使用。但是,TeamCity 無法執行這些測驗,但有以下例外:
[12:47:54][dotnet test] A total of 1 test files matched the specified pattern.
[12:47:56][dotnet test] NUnit Adapter 4.2.0.0: Test execution started
[12:47:56][dotnet test] Running all tests in C:\TeamCity\buildAgent\work\c9a5fb5bca7f6666\Tests\E2ETests\bin\Release\net6.0-windows\MyApp.E2ETests.dll
[12:47:58][dotnet test] NUnit3TestExecutor discovered 10 of 10 NUnit test cases using Current Discovery mode, Non-Explicit run
[12:48:07][dotnet test] Setup failed for test fixture MyApp.E2ETests.SomeTestsClass
[12:48:07][dotnet test] System.InvalidOperationException : Sequence contains no elements
[12:48:07][dotnet test] StackTrace: at System.Linq.ThrowHelper.ThrowNoElementsException()
[12:48:07][dotnet test] at System.Linq.Enumerable.Last[TSource](IEnumerable`1 source)
[12:48:07][dotnet test] at MyApp.E2ETests.Infrastructure.RealTestServerWebAppFactory`1.CreateHost(IHostBuilder builder) in C:\TeamCity\buildAgent\work\c9a5fb5bca7f6666\Tests\MyApp.E2ETests\Infrastructure\RealTestServerWebAppFactory.cs:line 85
[12:48:07][dotnet test] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.ConfigureHostBuilder(IHostBuilder hostBuilder)
[12:48:07][dotnet test] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
[12:48:07][dotnet test] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
[12:48:07][dotnet test] at MyApp.E2ETests.Infrastructure.RealTestServerWebAppFactory`1.EnsureServer() in C:\TeamCity\buildAgent\work\c9a5fb5bca7f6666\Tests\MyApp.E2ETests\Infrastructure\RealTestServerWebAppFactory.cs:line 122
[12:48:07][dotnet test] at MyApp.E2ETests.Infrastructure.RealTestServerWebAppFactory`1.get_ServerAddress() in C:\TeamCity\buildAgent\work\c9a5fb5bca7f6666\Tests\MyApp.E2ETests\Infrastructure\RealTestServerWebAppFactory.cs:line 35
System.InvalidOperationException : Sequence contains no elements
似乎這段代碼:
var addresses = server.Features.Get<IServerAddressesFeature>();
回傳由 TeamCity 運行的地址的空連接。因此,這段代碼:
ClientOptions.BaseAddress = addresses!.Addresses
.Select(x => new Uri(x))
.Last();
失敗System.InvalidOperationException : Sequence contains no elements。
奇怪的是,如果我使用與dotnet testTeamCity 手動使用相同的命令運行這些測驗cmd,它就可以作業。它在同一臺 CI 機器上作業,但從 手動執行cmd,而不是作為 TeamCity 的構建步驟。CI 機器運行 Windows Server 2019。
我一直認為這可能與用于運行 TeamCity 的用戶的權利/權限有關。但是,TeamCity 的 Windows 服務使用“本地系統”帳戶,因此據我所知,它應該具有所有權限。
任何幫助/想法表示贊賞:)
uj5u.com熱心網友回復:
根據這個答案,您必須IServerAddressesFeature使用以下代碼設定默認地址:
var serverFeatures = featureCollection.Get<IServerAddressesFeature>();
if (serverFeatures.Addresses.Count == 0)
{
ListenOn(DefaultAddress); // Start the server on the default address
serverFeatures.Addresses.Add(DefaultAddress) // Add the default address to the IServerAddressesFeature
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/484653.html
標籤:C# 。网 单元测试 asp.net 核心 .net-core
上一篇:如何在C#單元測驗中手動存根函式
