Atualizando para .NET 8, atualizando para IHostBuilder e executando testes do Playwright dentro do NUnit sem cabeçalho ou com cabeçalho em qualquer sistema operacional



Atualizando para .NET 8, atualizando para IHostBuilder e executando testes do Playwright dentro do NUnit sem cabeçalho ou com cabeçalho em qualquer sistema operacionalEu tenho feito não apenas testes unitários para meus websites, mas também testes de integração e testes de automação de navegador completos desde 2007 com Selenium. Ultimamente, no entanto, tenho usado o mais rápido e geralmente mais compatível Dramaturgo. Ele tem uma API e pode testar no Home windows, Linux, Mac, localmente, em um contêiner (headless), no meu pipeline de CI/CD, no Azure DevOps ou no GitHub Actions.

Para mim, é aquele último momento da verdade para garantir que o website funcione completamente, de ponta a ponta.

Posso escrever esses testes do Playwright em algo como TypeScript, e poderia iniciá-los com node, mas gosto de executar testes unitários finais e usar esse executor de teste e o take a look at harness como meu ponto de partida para meus aplicativos .NET. Estou acostumado a clicar com o botão direito e “executar testes unitários” ou, melhor ainda, clicar com o botão direito e “depurar testes unitários” no Visible Studio ou VS Code. Isso me dá o benefício de todas as asserções de uma estrutura de teste unitário completa e todos os benefícios de usar algo como o Playwright para automatizar meu navegador.

Em 2018 eu estava usando WebApplicationFactory e alguns hacks complicados para basicamente rodar o ASP.NET dentro do .NET (na época) Core 2.1 dentro dos testes unitários e então lançar o Selenium. Isso period meio instável e exigiria iniciar manualmente um processo separado e gerenciar seu ciclo de vida. No entanto, continuei com esse hack por vários anos basicamente tentando fazer o Kestrel Internet Server rodar dentro dos meus testes unitários.

Atualizei recentemente meu website principal e o website de podcast para o .NET 8. Tenha em mente que tenho movido meus websites das primeiras versões do .NET para as versões mais recentes. O weblog está rodando alegremente no Linux em um contêiner no .NET 8, mas seu código authentic começou em 2002 no .NET 1.1.

Agora que estou no .NET 8, descobri escandalosamente (quando meus testes de unidade pararam de funcionar) que o resto do mundo migrou do IWebHostBuilder para o IHostBuilder há cinco versões do .NET. Engula. Diga o que quiser, mas a compatibilidade com versões anteriores é impressionante.

Como tal, meu código para Program.cs mudou deste

public static void Principal(string() args)
{
CreateWebHostBuilder(args).Construct().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string() args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();

para isso:

public static void Principal(string() args)
{
CreateHostBuilder(args).Construct().Run();
}

public static IHostBuilder CreateHostBuilder(string() args) =>
Host.CreateDefaultBuilder(args).
ConfigureWebHostDefaults(WebHostBuilder => WebHostBuilder.UseStartup());

Não é uma grande mudança por fora, mas arruma as coisas por dentro e me prepara para um host genérico mais flexível para meu aplicativo da net.

Meus testes de unidade pararam de funcionar porque meu hack do Kestral Internet Server não estava mais inicializando meu servidor.

Aqui está um exemplo do meu objetivo da perspectiva de um dramaturgo dentro de um teste .NET NUnit.

(Check)
public async Job DoesSearchWork()
{
await Web page.GotoAsync(Url);

await Web page.Locator("#topbar").GetByRole(AriaRole.Hyperlink, new() { Identify = "episodes" }).ClickAsync();

await Web page.GetByPlaceholder("search and filter").ClickAsync();

await Web page.GetByPlaceholder("search and filter").TypeAsync("spouse");

const string visibleCards = ".showCard:seen";

var ready = await Web page.WaitForSelectorAsync(visibleCards, new PageWaitForSelectorOptions() { Timeout = 500 });

await Count on(Web page.Locator(visibleCards).First).ToBeVisibleAsync();

await Count on(Web page.Locator(visibleCards)).ToHaveCountAsync(5);
}

Adoro isso. Authorized e limpo. Certamente aqui estamos assumindo que temos uma URL naquela primeira linha, que será localhost alguma coisa, e então assumimos que nosso aplicativo net foi iniciado por conta própria.

Aqui está o código de configuração que inicia minha nova “fábrica de construtores de testes de aplicativos da net”, sim, o nome é estúpido, mas é descritivo. Observe o OneTimeSetUp e o OneTimeTearDown. Isso inicia meu aplicativo da net dentro do contexto do meu TestHost. Observe que o :0 faz o aplicativo encontrar uma porta que eu, infelizmente, tenho que desenterrar e colocar na Url non-public para uso em meus testes de unidade. Observe que o na verdade é minha classe Startup dentro de Startup.cs que hospeda o pipeline do meu aplicativo e Configure e ConfigureServices são configurados aqui para que o roteamento funcione.

non-public string Url;
non-public WebApplication? _app = null;

(OneTimeSetUp)
public void Setup()
{
var builder = WebApplicationTestBuilderFactory.CreateBuilder();

var startup = new Startup(builder.Setting);
builder.WebHost.ConfigureKestrel(o => o.Pay attention(IPAddress.Loopback, 0));
startup.ConfigureServices(builder.Providers);
_app = builder.Construct();

// pay attention on any native port (therefore the 0)
startup.Configure(_app, _app.Configuration);
_app.Begin();

//you might be kidding me
Url = _app.Providers.GetRequiredService().Options.GetRequiredFeature().Addresses.Final();
}

(OneTimeTearDown)
public async Job TearDown()
{
await _app.DisposeAsync();
}

Então, que horrores estão enterrados no WebApplicationTestBuilderFactory? A primeira parte é ruim e devemos consertá-la para o .NET 9. O resto é realmente muito bom, com uma dica para David Fowler por sua ajuda e orientação! Esta é a mágica e o nojento em uma pequena classe auxiliar.

public class WebApplicationTestBuilderFactory 
{
public static WebApplicationBuilder CreateBuilder() the place T : class
{
//This ungodly code requires an unused reference to the MvcTesting package deal that hooks up
// MSBuild to create the manifest file that's learn right here.
var testLocation = Path.Mix(AppContext.BaseDirectory, "MvcTestingAppManifest.json");
var json = JsonObject.Parse(File.ReadAllText(testLocation));
var asmFullName = typeof(T).Meeting.FullName ?? throw new InvalidOperationException("Meeting Full Identify is null");
var contentRootPath = json?(asmFullName)?.GetValue();

//spin up an actual dwell net utility inside TestHost.exe
var builder = WebApplication.CreateBuilder(
new WebApplicationOptions()
{
ContentRootPath = contentRootPath,
ApplicationName = asmFullName
});
return builder;
}
}

As primeiras 4 linhas são desagradáveis. Como o teste é executado no contexto de um diretório diferente e meu website precisa ser executado dentro do contexto de seu próprio caminho raiz de conteúdo, tenho que forçar o caminho raiz de conteúdo a estar correto e a única maneira de fazer isso é obtendo o diretório base dos aplicativos de um arquivo gerado dentro do MSBuild do pacote (antigo) MvcTesting. O pacote não é usado, mas ao referenciá-lo, ele entra na compilação e cria aquele arquivo que eu uso para extrair o diretório.

Se pudermos nos livrar desse “hack” e extrair o diretório do contexto em outro lugar, essa função auxiliar se transformará em uma única linha e o .NET 9 ficará MUITO, MUITO mais testável!

Agora posso executar meus testes unitários E testes de integração do navegador Playwright em todos os sistemas operacionais, com ou sem cabeça, no docker ou no steel. O website foi atualizado para .NET 8 e está tudo certo com meu código. Bem, pelo menos ele roda. 😉




Sobre Scott

Scott Hanselman é um ex-professor, ex-arquiteto-chefe em finanças, agora palestrante, consultor, pai, diabético e funcionário da Microsoft. Ele é um comediante de stand-up fracassado, um cornrower e um autor de livros.

Facebook
Twitter
se inscrever
Sobre Boletim de Notícias

Hospedagem por
Hospedado em um Serviço de Aplicativo do Azure










Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *