使用WebApplicationFactory和Playwright在.Net 6/7中编写集成测试。

huangapple go评论101阅读模式
英文:

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:

  1. protected override IHost CreateHost(IHostBuilder builder)
  2. {
  3. // Create the host for TestServer now before we
  4. // modify the builder to use Kestrel instead.
  5. var testHost = builder.Build();
  6. // Modify the host builder to use Kestrel instead
  7. // of TestServer so we can listen on a real address.
  8. builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
  9. // Create and start the Kestrel server before the test server,
  10. // otherwise due to the way the deferred host builder works
  11. // for minimal hosting, the server will not get "initialized
  12. // enough" for the address it is listening on to be available.
  13. // See https://github.com/dotnet/aspnetcore/issues/33846.
  14. _host = builder.Build();
  15. _host.Start();
  16. // Extract the selected dynamic port out of the Kestrel server
  17. // and assign it onto the client options for convenience so it
  18. // "just works" as otherwise it'll be the default http://localhost
  19. // URL, which won't route to the Kestrel-hosted HTTP server.
  20. var server = _host.Services.GetRequiredService<IServer>();
  21. var addresses = server.Features.Get<IServerAddressesFeature>();
  22. ClientOptions.BaseAddress = addresses!.Addresses
  23. .Select(x => new Uri(x))
  24. .Last();
  25. // Return the host that uses TestServer, rather than the real one.
  26. // Otherwise the internals will complain about the host's server
  27. // not being an instance of the concrete type TestServer.
  28. // See https://github.com/dotnet/aspnetcore/pull/34702.
  29. testHost.Start();
  30. return testHost;
  31. }

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:

  1. protected override IHost CreateHost(IHostBuilder builder)
  2. {
  3. // Create the host for TestServer now before we
  4. // modify the builder to use Kestrel instead.
  5. var testHost = builder.Build();
  6. // Modify the host builder to use Kestrel instead
  7. // of TestServer so we can listen on a real address.
  8. //builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
  9. // Create and start the Kestrel server before the test server,
  10. // otherwise due to the way the deferred host builder works
  11. // for minimal hosting, the server will not get "initialized
  12. // enough" for the address it is listening on to be available.
  13. // See https://github.com/dotnet/aspnetcore/issues/33846.
  14. Host = Program.CreateHostBuilder(null)
  15. .ConfigureWebHost(ConfigureWebHost)
  16. .Build();
  17. Host.Start();
  18. // Extract the selected dynamic port out of the Kestrel server
  19. // and assign it onto the client options for convenience so it
  20. // "just works" as otherwise it'll be the default http://localhost
  21. // URL, which won't route to the Kestrel-hosted HTTP server.
  22. var server = Host.Services.GetRequiredService<Microsoft.AspNetCore.Hosting.Server.IServer>();
  23. var addresses = server.Features.Get<IServerAddressesFeature>();
  24. ClientOptions.BaseAddress = addresses!.Addresses
  25. .Select(x => new Uri(x))
  26. .Last();
  27. // Return the host that uses TestServer, rather than the real one.
  28. // Otherwise the internals will complain about the host's server
  29. // not being an instance of the concrete type TestServer.
  30. // See https://github.com/dotnet/aspnetcore/pull/34702.
  31. testHost.Start();
  32. return testHost;
  33. }

Notice I created the IHost directly from the Program class.

Now when I run the following lines in my test:

  1. var playwright = await Playwright.CreateAsync();
  2. var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = false, SlowMo = 50 });
  3. var page = await browser.NewPageAsync();
  4. 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 之前创建,然后迁移,因此仍然使用了"旧"的初始化方式:

  1. Host.CreateDefaultBuilder(args)
  2. .ConfigureWebHostDefaults(webBuilder =>
  3. {
  4. // 这里的代码...
  5. }

这不会起作用。相反,IHostBuilder 被新的 WebApplication 取代:

  1. 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:

  1. Host.CreateDefaultBuilder(args)
  2. .ConfigureWebHostDefaults(webBuilder =>
  3. {
  4. // Code here...
  5. }

This will not work. Instead, the IHostBuilder is replaced by the new WebApplication

  1. 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.

huangapple
  • 本文由 发表于 2023年5月17日 22:57:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76273485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定