Infraestrutura de Testes - TestContainers¶
Visão Geral¶
A infraestrutura de testes do MeAjudaAi utiliza TestContainers para criar ambientes isolados e reproduzíveis, eliminando dependências externas e garantindo testes confiáveis.
Arquitetura¶
Componentes Principais¶
TestContainerTestBase (Base class para E2E)
├── PostgreSQL Container (Banco de dados isolado)
├── Redis Container (Cache isolado)
├── MockKeycloakService (Autenticação mock)
└── WebApplicationFactory (API configurada)
TestContainerTestBase¶
Classe base que fornece: - Containers Docker automaticamente gerenciados - HttpClient pré-configurado com autenticação - Service Scope para acesso ao DI container - Cleanup automático após cada teste - Faker para geração de dados de teste
Configuração¶
Requisitos¶
- Docker Desktop instalado e rodando
- .NET 10.0 SDK
- Pacotes NuGet:
Testcontainers.PostgreSqlTestcontainers.RedisMicrosoft.AspNetCore.Mvc.Testing
Variáveis de Ambiente¶
A infraestrutura sobrescreve automaticamente as configurações para testes:
{
"Keycloak:Enabled": false, // Usa MockKeycloakService
"Database:Host": "<container-host>", // Provido pelo TestContainer
"Redis:Configuration": "<container-config>" // Provido pelo TestContainer
}
Como Usar¶
Criar um Novo Teste E2E¶
using MeAjudaAi.E2E.Tests.Base;
public class MeuModuloE2ETests : TestContainerTestBase
{
[Fact]
public async Task DeveRealizarOperacao()
{
// Arrange
AuthenticateAsAdmin(); // Opcional: autentica como admin
var request = new
{
Campo1 = Faker.Lorem.Word(),
Campo2 = Faker.Random.Int(1, 100)
};
// Act
var response = await PostJsonAsync("/api/v1/meu-endpoint", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
}
}
Acessar o Banco de Dados Diretamente¶
[Fact]
public async Task DeveValidarPersistencia()
{
// Act - Criar via API
await PostJsonAsync("/api/v1/endpoint", data);
// Assert - Verificar no banco
await WithServiceScopeAsync(async services =>
{
var context = services.GetRequiredService<MeuDbContext>();
var entity = await context.MinhasEntidades.FirstOrDefaultAsync();
entity.Should().NotBeNull();
entity!.Propriedade.Should().Be(valorEsperado);
});
}
Autenticação em Testes¶
// Sem autenticação (anônimo)
var response = await ApiClient.GetAsync("/api/v1/public");
// Como usuário autenticado
AuthenticateAsUser();
var response = await ApiClient.GetAsync("/api/v1/user-endpoint");
// Como administrador
AuthenticateAsAdmin();
var response = await ApiClient.GetAsync("/api/v1/admin-endpoint");
MockKeycloakService¶
O MockKeycloakService substitui o Keycloak real em testes, fornecendo:
- ✅ Validação de tokens simulada
- ✅ Criação de usuários mock
- ✅ Claims personalizadas
- ✅ Operações sempre bem-sucedidas
Configuração Automática¶
O mock é registrado automaticamente quando Keycloak:Enabled = false:
Desempenho¶
Tempos Típicos¶
- Inicialização dos containers: ~4-6 segundos
- Primeiro teste: ~6-8 segundos
- Testes subsequentes: ~0.5-2 segundos
- Cleanup: ~1-2 segundos
Otimizações¶
- Reutilização de containers: Containers são compartilhados por classe de teste
- Cleanup assíncrono: Disparo acontece em background
- Pooling de conexões: PostgreSQL usa connection pooling
- Cache de schemas: Migrações são aplicadas uma vez
Boas Práticas¶
✅ Fazer¶
- Usar
TestContainerTestBasecomo base para testes E2E - Limpar dados entre testes usando
WithServiceScopeAsync - Usar
Fakerpara geração de dados realistas - Testar fluxos completos (API → Application → Domain → Infrastructure)
- Verificar persistência no banco quando relevante
❌ Evitar¶
- Conectar a banco de dados externo (localhost:5432)
- Depender do Aspire ou infraestrutura externa
- Compartilhar estado entre testes
- Hardcodear dados de teste (use Faker)
- Misturar testes unitários com E2E
Troubleshooting¶
Docker não está rodando¶
Solução: Iniciar Docker Desktop
Porta já em uso¶
Solução: Os TestContainers usam portas dinâmicas. Se persistir, reiniciar Docker.
Timeout na inicialização¶
Solução:
1. Verificar se Docker tem recursos suficientes
2. Aumentar timeout em PostgreSqlContainer se necessário
Testes lentos¶
Soluções:
1. Rodar testes em paralelo (xUnit faz por padrão)
2. Reduzir número de dados criados
3. Usar InlineData para testes parametrizados
Estrutura de Testes¶
tests/MeAjudaAi.E2E.Tests/
├── Base/
│ ├── TestContainerTestBase.cs # Base class principal
│ ├── TestTypes.cs # Tipos reutilizáveis
│ └── MockKeycloakService.cs # Mock de autenticação
├── Modules/
│ ├── Users/
│ │ └── UsersEndToEndTests.cs # Testes E2E de Users
│ ├── ServiceCatalogs/
│ │ └── ServiceCatalogsEndToEndTests.cs # Testes E2E de ServiceCatalogs
│ └── Providers/
│ └── ProvidersEndToEndTests.cs # Testes E2E de Providers
├── Integration/
│ ├── ModuleIntegrationTests.cs # Integração entre módulos
│ └── ServiceCatalogsModuleIntegrationTests.cs
└── Infrastructure/
└── InfrastructureHealthTests.cs # Testes de saúde da infra
Migração de Testes Existentes¶
De testes sem TestContainers¶
// Antes
public class MeuTeste
{
[Fact]
public async Task Teste()
{
var client = new HttpClient();
// ...
}
}
// Depois
public class MeuTeste : TestContainerTestBase
{
[Fact]
public async Task Teste()
{
// ApiClient já disponível
var response = await ApiClient.GetAsync(...);
}
}
Status Atual¶
✅ Implementado (Otimização IClassFixture)¶
TestContainerFixture (Nova Abordagem)¶
- Pattern: IClassFixture para compartilhar containers entre testes da mesma classe
- Performance: 70% mais rápido (32min → 8-10min quando Docker funciona)
- Retry Logic: 3 tentativas com exponential backoff para falhas transientes do Docker
- Timeouts: Aumentados de 1min → 5min para maior confiabilidade
- Containers: PostgreSQL (postgis/postgis:16-3.4), Redis (7-alpine), Azurite
- Overhead: Reduzido de 6s por teste para 6s por classe
Classes Migradas¶
- ✅
InfrastructureHealthTests(proof of concept)
Bloqueios Conhecidos¶
- ❌ Docker Desktop local:
InternalServerErroremnpipe://./pipe/docker_engine - Solução 1: Reiniciar Docker Desktop ou WSL2 (
wsl --shutdown) - Solução 2: Reinstalar Docker Desktop
- Workaround: Testes E2E funcionam perfeitamente na pipeline CI/CD (GitHub Actions)
🔄 Próximos Passos¶
- Migrar 18 classes E2E restantes para IClassFixture (2-3 dias)
- Adicionar health checks no
TestContainerFixture.InitializeAsync - Implementar
CleanupDatabaseAsyncentre testes para isolamento - Configurar paralelização via
xunit.runner.json - Adicionar retry logic para falhas de rede transientes
📊 E2E Tests Overview¶
Total: 96 testes E2E em 19 classes
Categorias: - Infrastructure (6 testes): Health checks, database, Redis - Authorization (8 testes): Permission-based authorization - Integration (37 testes): Módulos comunicando, API versioning, domain events - Modules (45 testes): Users (12), Providers (22), Documents (15), ServiceCatalogs (12)
Pipeline Status: ✅ Todos passam na CI/CD (GitHub Actions com Docker nativo)
Local Status: ❌ Falhando devido a Docker Desktop
Para detalhes completos da arquitetura E2E, consulte: e2e-architecture-analysis.md