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:

  1. Criar o projeto de teste na solution com o framework de sua preferência (xUnit ou NUnit);
  2. Instalar o pacote Nuget Microsoft.Aspnetcore.Mvc.Testing onde está a classe WebApplicationFactory;
  3. Referenciar o projeto de API para ter acesso à implementação que queremos testar;
  4. 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:

WebApplicationFactory no .NET 6

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

David Fowl – Twitter

Fernando Okuma

Desenvolvedor de aplicações de alto e baixo nível e aspirante a agilista.