Tenho feito não apenas testes de unidade para meus sites, mas também testes de integração e testes de automação de navegador desde 2007 com Selenium. Ultimamente, porém, tenho usado o mais rápido e geralmente mais compatível Dramaturgo. Possui uma API e pode testar em Windows, Linux, Mac, localmente, em um contêiner (headless), em meu pipeline de CI/CD, no Azure DevOps ou no GitHub Actions.
Para mim, é o último momento da verdade para garantir que o site funcione completamente de ponta a ponta.
Posso escrever esses testes do Playwright em algo como TypeScript e poderia iniciá-los com o node, mas gosto de executar testes de unidade final e usar esse executor de testes e equipamento de teste como ponto de partida para meus aplicativos .NET. Estou acostumado a clicar com o botão direito e “executar testes de unidade” ou, melhor ainda, clicar com o botão direito e “depurar testes de unidade” no Visual Studio ou VS Code. Isso me dá o benefício de todas as afirmações de uma estrutura completa de testes de unidade e todos os benefícios de usar algo como o Playwright para automatizar meu navegador.
Em 2018 eu estava usando WebApplicationFactory e alguns truques complicados para basicamente ativar o ASP.NET no .NET (na época) Core 2.1 nos testes de unidade e, em seguida, iniciar o Selenium. Isso era meio complicado 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 com que o Kestrel Web Server funcionasse dentro dos meus testes de unidade.
Recentemente, atualizei meu site principal e meu site de podcast para o .NET 8. Lembre-se de que estou migrando meus sites das primeiras versões do .NET para as versões mais recentes. O blog funciona felizmente no Linux em um contêiner no .NET 8, mas seu código original começou em 2002 no .NET 1.1.
Agora que estou no .NET 8, descobri escandalosamente (pois meus testes de unidade pararam de funcionar) que o resto do mundo mudou do IWebHostBuilder para o IHostBuilder cinco versões do .NET atrás. Gole. 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 Main(string() args)
{
CreateWebHostBuilder(args).Build().Run();
}public static IWebHostBuilder CreateWebHostBuilder(string() args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
para isso:
public static void Main(string() args)
{
CreateHostBuilder(args).Build().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 um host genérico mais flexível para meu aplicativo web.
Meus testes de unidade pararam de funcionar porque meu hack do Kestral Web Server não estava mais iniciando meu servidor.
Aqui está um exemplo do meu objetivo da perspectiva do Playwright em um teste .NET NUnit.
(Test)
public async Task DoesSearchWork()
{
await Page.GotoAsync(Url);await Page.Locator("#topbar").GetByRole(AriaRole.Link, new() { Name = "episodes" }).ClickAsync();
await Page.GetByPlaceholder("search and filter").ClickAsync();
await Page.GetByPlaceholder("search and filter").TypeAsync("wife");
const string visibleCards = ".showCard:visible";
var waiting = await Page.WaitForSelectorAsync(visibleCards, new PageWaitForSelectorOptions() { Timeout = 500 });
await Expect(Page.Locator(visibleCards).First).ToBeVisibleAsync();
await Expect(Page.Locator(visibleCards)).ToHaveCountAsync(5);
}
Eu amo isto. Agradável e limpo. Certamente aqui estamos assumindo que temos uma URL na primeira linha, que será localhost, e então assumimos que nosso aplicativo da web 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 web”. Sim, o nome é estúpido, mas é descritivo. Observe o OneTimeSetUp e o OneTimeTearDown. Isso inicia meu aplicativo da web no contexto do meu TestHost. Observe que :0 faz com que o aplicativo encontre uma porta que, infelizmente, tenho que desenterrar e colocar no URL privado para uso em meus testes de unidade. Observe que o
private string Url;
private WebApplication? _app = null;(OneTimeSetUp)
public void Setup()
{
var builder = WebApplicationTestBuilderFactory.CreateBuilder(); var startup = new Startup(builder.Environment);
builder.WebHost.ConfigureKestrel(o => o.Listen(IPAddress.Loopback, 0));
startup.ConfigureServices(builder.Services);
_app = builder.Build();// listen on any local port (hence the 0)
startup.Configure(_app, _app.Configuration);
_app.Start();//you are kidding me
Url = _app.Services.GetRequiredService().Features.GetRequiredFeature ().Addresses.Last();
}(OneTimeTearDown)
public async Task TearDown()
{
await _app.DisposeAsync();
}
Então, que horrores estão enterrados no WebApplicationTestBuilderFactory? A primeira parte é ruim e devemos corrigi-la para o .NET 9. O resto é realmente muito bom, com uma gorjeta para David Fowler por sua ajuda e orientação! Esta é a magia e o problema de uma pequena classe auxiliar.
public class WebApplicationTestBuilderFactory
{
public static WebApplicationBuilder CreateBuilder() where T : class
{
//This ungodly code requires an unused reference to the MvcTesting package that hooks up
// MSBuild to create the manifest file that is read here.
var testLocation = Path.Combine(AppContext.BaseDirectory, "MvcTestingAppManifest.json");
var json = JsonObject.Parse(File.ReadAllText(testLocation));
var asmFullName = typeof(T).Assembly.FullName ?? throw new InvalidOperationException("Assembly Full Name is null");
var contentRootPath = json?(asmFullName)?.GetValue(); //spin up a real live web application 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 site precisa ser executado no contexto de seu próprio caminho raiz de conteúdo, tenho que forçar o caminho raiz do conteúdo a estar correto e a única maneira de fazer isso é obtendo o diretório base de aplicativos de um arquivo gerado no MSBuild do pacote MvcTesting (antigo). 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 de outro lugar, essa função auxiliar se transformará em uma única linha e o .NET 9 ficará MUITO mais testável!
Agora posso executar meus testes de unidade E testes de integração do navegador Playwright em todos os sistemas operacionais, com ou sem cabeça, no docker ou no metal. O site está atualizado para .NET 8 e está tudo certo com meu código. Bem, pelo menos funciona. 😉
Sobre Scott
Scott Hanselman é ex-professor, ex-arquiteto-chefe em finanças, agora palestrante, consultor, pai, diabético e funcionário da Microsoft. Ele é um comediante stand-up fracassado, um cornrower e autor de livros.
Deseja saber mais sobre Programação e Desenvolvimento Clique Aqui!
ASP.NET,DotNetCore

Perito em Computação Forense e Crimes Cibernéticos
Investigação Digital | Laudos Técnicos | Resposta a Incidentes
Bacharel em Sistemas da Informação, Certificado Microsoft Azure IA e MOS. Trabalho como Administrador de Redes, Firewall e Servidores Windows e Linux!
Minhas atividades favoritas são: Caminhar, Fazer Trilhas, Natureza, Insetos e claro ler sobre Tecnologia.


