英文:
Writing Integration tests with WebApplicationFactory and Playwright in .Net 6/7
问题
我整天都被这个问题困扰,找不到解决办法。我试图解决的问题与这篇文章中所解决的问题完全相同。
这也与这个问题有关,它有一个非常相似的解决方案,尽管在这个问题中他们使用了Selenium。
解决方案基本上是覆盖WebApplicationfactory<Program>
中的CreateHost
方法,如下所示:
(以下为代码部分)
然而,我在调用IHostBuilder
中的Build()
方法时遇到“Build can only be called once”错误。其他人在博客文章中也评论说他们遇到了这个错误,但没有找到解决方法。
我阅读了评论和在线文档,并尝试进行了以下修改:
(以下为修改后的代码部分)
请注意,我直接从Program
类中创建了IHost
。
现在当我在测试中运行以下行时:
(以下为测试代码部分)
我实际上可以看到Playwright打开了一个Chrome页面(如果没有上面的CreateHost
覆盖,这是不会发生的)。然而,以这种方式运行应用程序时,它并不工作,给我一个没有更多信息的Fatal页面。
注:当我只启动它时,Blazor应用程序正常运行。它目前在.Net 6上。测试项目目前在.Net 7上。我尝试切换.Net版本并相应地更新包,但问题仍然存在。
感谢任何帮助!
英文:
I've been stuck with this issue the whole day and I can't find a solution.
What I am trying to solve is exactly the same problem that is supposedly solved in this Article.
It is also related to this issue with a very similar solution. Although in this issue they are using Selenium.
The solution is basically overriding the CreateHost
method from WebApplicationfactory<Program>
as demonstrated here:
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;
}
I am however, getting an error “Build can only be called once” at the moment I call the Build()
method in the IHostBuilder
. Other people also have commented in the blog post that they got this error but no response was found.
I read through the comments and documentation online and tried to modify for the following:
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 = Program.CreateHostBuilder(null)
.ConfigureWebHost(ConfigureWebHost)
.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<Microsoft.AspNetCore.Hosting.Server.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;
}
Notice I created the IHost
directly from the Program class.
Now when I run the following lines in my test:
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = false, SlowMo = 50 });
var page = await browser.NewPageAsync();
await page.GotoAsync(webApplicationSuitFixture.BaseUrl);
I can actually see that Playwright opens a Chrome page (Which would not happen without the CreateHost
override above). However the application is not running this way, it gives me a Fatal page without much more to go on.
Obs: The blazor app runs normally when I just start it. It is currently on .Net 6. The test project is currently in .Net 7. I tried to switch the .Net versions and update the packages accordingly, but the same problem persists.
Any help is appreciated!
答案1
得分: 0
在进一步测试后,我发现问题实际上出现在测试的目标项目上。
该项目在 .Net 6 之前创建,然后迁移,因此仍然使用了"旧"的初始化方式:
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// 这里的代码...
}
这不会起作用。相反,IHostBuilder
被新的 WebApplication
取代:
var builder = WebApplication.CreateBuilder(args);
这将允许多次调用 Build()
方法。并且 Daniel Donbavand 提出的解决方案 将起作用。
英文:
After more testing, I figure out that the problem was actually on the target project of the tests.
The project was created prior to .Net 6 and then migrated, therefore it still had the "Old" Initialization from Host:
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// Code here...
}
This will not work. Instead, the IHostBuilder
is replaced by the new WebApplication
var builder = WebApplication.CreateBuilder(args);
This will allow the call of the Build()
to be performed more than once. And the solution proposed by Daniel Donbavand will work.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论