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

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

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.

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:

确定