Teste de Integração C# com Docker: Nível Avançado
\nO teste de integração é uma etapa fundamental para garantir a qualidade em aplicações .NET modernas. O uso do Docker permite criar ambientes isolados e reproduzíveis, o mais próximo possível da produção. Neste artigo, exploraremos técnicas avançadas de teste de integração C# com Docker: desde a configuração do Testcontainers até a organização de pipelines de CI/CD.
\nO material é voltado para desenvolvedores que já conhecem os fundamentos do teste unitário e os conceitos básicos do Docker. Aprofundaremos o gerenciamento do ciclo de vida dos contêineres, o trabalho com vários serviços, a depuração e a otimização do tempo de execução dos testes.
\n\n1. Configuração do Testcontainers para .NET
\nTestcontainers é uma biblioteca que gerencia contêineres Docker a partir do código de teste. Ela cria, inicia e para contêineres automaticamente, garantindo o isolamento de cada teste.
\nPara começar, instale o pacote no seu projeto de teste:
\ndotnet add package Testcontainers\nExemplo básico — testando um repositório com PostgreSQL:
\nusing Testcontainers.PostgreSql;\nusing Xunit;\n\npublic class UserRepositoryTests : IAsyncLifetime\n{\n private readonly PostgreSqlContainer _postgresContainer;\n\n public UserRepositoryTests()\n {\n // Configura o contêiner com PostgreSQL\n _postgresContainer = new PostgreSqlBuilder()\n .WithImage("postgres:15")\n .WithDatabase("testdb")\n .WithUsername("test")\n .WithPassword("test123")\n .WithCleanUp(true)\n .Build();\n }\n\n public async Task InitializeAsync()\n {\n // Inicia o contêiner antes de todos os testes\n await _postgresContainer.StartAsync();\n }\n\n public async Task DisposeAsync()\n {\n // Para e remove o contêiner\n await _postgresContainer.DisposeAsync();\n }\n\n [Fact]\n public async Task AddUser_ShouldCreateUser()\n {\n // Obtém a string de conexão do contêiner\n var connectionString = _postgresContainer.GetConnectionString();\n var repository = new UserRepository(connectionString);\n\n await repository.AddUserAsync(new User { Name = "Test User" });\n var user = await repository.GetUserByNameAsync("Test User");\n\n Assert.NotNull(user);\n Assert.Equal("Test User", user.Name);\n }\n}\nImportante: use IAsyncLifetime para gerenciar o contêiner. Isso garante que cada classe de teste receba uma instância nova do banco de dados.
2. Arquitetura Multicontêiner com Docker Compose
\nAplicações reais dependem de vários serviços: banco de dados, fila de mensagens, cache. O Testcontainers suporta Docker Compose, permitindo executar uma infraestrutura completa.
\nInstale o pacote adicional:
\ndotnet add package Testcontainers.Compose\nExemplo de arquivo docker-compose.test.yml:
version: '3.8'\nservices:\n postgres:\n image: postgres:15\n environment:\n POSTGRES_USER: test\n POSTGRES_PASSWORD: test\n POSTGRES_DB: testdb\n redis:\n image: redis:7-alpine\nUso no teste:
\nusing Testcontainers.Compose;\n\npublic class OrderServiceTests\n{\n private readonly DockerComposeContainer _compose;\n\n public OrderServiceTests()\n {\n _compose = new DockerComposeContainer()\n .WithComposeFilePath("docker-compose.test.yml")\n .WithEnvironment("ASPNETCORE_ENVIRONMENT", "Testing")\n .Build();\n }\n\n [Fact]\n public async Task CreateOrder_ShouldSaveToDbAndCache()\n {\n await _compose.StartAsync();\n \n var dbConnection = _compose.GetConnectionString("postgres", 5432);\n var redisConnection = _compose.GetConnectionString("redis", 6379