Pruebas de integración de C# con Docker: nivel avanzado
\nLas pruebas de integración son una etapa clave para garantizar la calidad en aplicaciones .NET modernas. El uso de Docker permite crear entornos aislados y reproducibles, muy cercanos a producción. En este artículo, exploraremos técnicas avanzadas de pruebas de integración de C# con Docker: desde la configuración de Testcontainers hasta la organización de pipelines de CI/CD.
\nEl material está dirigido a desarrolladores que ya conocen los fundamentos de las pruebas unitarias y los conceptos básicos de Docker. Profundizaremos en la gestión del ciclo de vida de los contenedores, el trabajo con múltiples servicios, la depuración y la optimización del tiempo de ejecución de las pruebas.
\n\n1. Configuración de Testcontainers para .NET
\nTestcontainers es una biblioteca que gestiona contenedores Docker desde el código de las pruebas. Crea, inicia y detiene contenedores automáticamente, garantizando el aislamiento de cada prueba.
\nPara empezar, instale el paquete en su proyecto de prueba:
\ndotnet add package Testcontainers\nEjemplo básico: probar un repositorio con PostgreSQL:
\nusing Testcontainers.PostgreSql;\nusing Xunit;\n\npublic class UserRepositoryTests : IAsyncLifetime\n{\n private readonly PostgreSqlContainer _postgresContainer;\n\n public UserRepositoryTests()\n {\n // Configuramos el contenedor con 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 // Iniciamos el contenedor antes de todas las pruebas\n await _postgresContainer.StartAsync();\n }\n\n public async Task DisposeAsync()\n {\n // Detenemos y eliminamos el contenedor\n await _postgresContainer.DisposeAsync();\n }\n\n [Fact]\n public async Task AddUser_ShouldCreateUser()\n {\n // Obtenemos la cadena de conexión al contenedor\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 gestionar el contenedor. Esto garantiza que cada clase de prueba reciba una instancia nueva de la base de datos.
2. Arquitectura multicontenedor con Docker Compose
\nLas aplicaciones reales dependen de varios servicios: bases de datos, colas de mensajes, cachés. Testcontainers admite Docker Compose, lo que permite ejecutar una infraestructura completa.
\nInstale el paquete adicional:
\ndotnet add package Testcontainers.Compose\nEjemplo de archivo 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 en la prueba:
\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