Nos primeiros contatos com as novas features do .NET 6, um dos pontos que provavelmente mais chamam a atenção das pessoas que já estavam trabalhando com as versões anteriores, é a nova estruturação que o template de webapi gera. Agora, ela está mais enxuta e o famoso Startup.cs foi removido. Para quem já estava acostumado com o formato anterior, ao utilizar a classe WebApplicationFactory vai precisar fazer algumas coisas diferentes para que essa estrutura funcione.
Minimal APIs
Com a chegada do .NET 6, uma das grandes novidades é que o template de webapi gera um projeto mais enxuto e sem o famoso arquivo Startup.cs. Agora toda a configuração está diretamente no Program.cs e fica algo assim:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Para mais informações sobre o Minimal API consulte a documentação da Microsoft.
Testes de integração
Os testes de integração são muito comuns nos projetos dotnet. As pessoas que já estão acostumadas a escrever esse tipo de testes, ao utilizarem a classe WebApplicationFactory (disponível no pacote Nuget Microsoft.Aspnetcore.Mvc.Testing), vão notar que a nova maneira de criarmos a nossa aplicação eliminou exatamente o Statup.cs, onde estavam as informações que são necessárias para subir a nossa aplicação em memória. Aí vem a pergunta: E agora? O WebApplicationFactory consegue extrair as informações do Program.cs? Bom, a resposta é sim, mas tem um pequeno detalhe a mais.
Como falei acima, os testes de integração são comuns nos projetos dotnet e, para as pessoas que não estão acostumadas a fazer o Setup desse tipo de teste, vamos a um passo a passo:
- Criar o projeto de teste na solution com o framework de sua preferência (xUnit ou NUnit);
- Instalar o pacote Nuget Microsoft.Aspnetcore.Mvc.Testing onde está a classe WebApplicationFactory;
- Referenciar o projeto de API para ter acesso à implementação que queremos testar;
- Criar o objeto WebApplicationFactory no setup dos testes, passando a classe que contém a configuração da API.
É nesse quarto passo que os processos começam a ficar diferentes.
A configuração do teste que era assim:
var webApplicationFactory = new WebApplicationFactory<Startup>();
agora ficou assim:
var webApplicationFactory = new WebApplicationFactory<Program>();
Simples né!? Mas ai surge o seguinte erro para nos atormentar:
O projeto de testes não consegue acessar a classe Program porque ela é uma Internal Class. Felizmente é bem fácil de resolver, e existe mais de uma maneira de fazer isso.
Tornando a classe Program.cs acessível para o projeto de testes
Existem algumas maneiras de tornar a classe Program acessível ao projeto de testes, e podemos seguir a abordagem que faça mais sentido para o projeto
Criando o partial class
Uma das formas, que particularmente é a que menos gosto, é criar uma partial class do Program. Isso permite declararmos como public e consequentemente torná-la acessível no projeto de testes.
app.Run();
public partial class Program {}
Um dos problemas que vejo nessa abordagem é que a classe Program fica acessível não somente para o projeto de testes, mas também para qualquer projeto que faça referência à API, além de ficar um trecho de código praticamente morto
Utilizando Assembly Attributes
É possível utilizar o Assembly Atributes no inicio do arquivo Program.cs para tornar as classes acessíveis no projeto de testes.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("NomeProjetoDeTeste")]
var builder = WebApplication.CreateBuilder(args);
O ponto positivo dessa maneira de expor as classes privadas, do projeto da API para o projeto de testes, é que dessa forma fica bem claro como o setup do projeto de testes consegue acessar o Program.cs. O lado negativo, e nesse caso é uma opinião, essa declaração no meio do código tem como objetivo resolver um problema com os testes, mas não tem nenhuma utilidade para o a implementação da API e acaba ficando confuso para quem não sabe qual o objetivo dessa linha.
Utilizando o InternalsVisibleTo
Existe uma configuração que podemos fazer no arquivo csproj que torna as classes internas do projeto visíveis para outro projeto que especificamos. Isso resolve o problema de deixar a classe Program acessível para todos os projetos da solution sem necessidade. Para fazer isso só precisamos adicionar a seguinte configuração no csproj do projeto da APi.
<ItemGroup>
<InternalsVisibleTo Include="NomeProjetoDeTeste" />
</ItemGroup>
Acho essa solução mais interessante porque as classes internas ficam acessíveis somente para o projeto que irá, de fato, precisar do acesso, ao invés de adicionamos um código desnecessário no nosso projeto.
Apesar de um pouco diferente, é bom ver que a Microsoft se preocupou em ajustar as ferramentas para o novo formato do template de WebAPI. E mesmo não sendo muito óbvio o que precisa ser configurado, é uma solução relativamente simples para continuar utilizando esse recurso incrível que é o WebApplicationFactory.
Referências
Supporting integration tests with WebApplicationFactory in .NET 6
C# – How to setup WebApplicationFactory in .Net 6 without a startup.cs file