Skip to content

RabbitMQ

El RabbitMQContainer es un componente de la librería CodeDesignPlus.Net.xUnit que proporciona un contenedor Docker para ejecutar RabbitMQ, un broker de mensajería. Este componente facilita la creación de pruebas unitarias que dependen de RabbitMQ, ofreciendo un entorno de pruebas aislado y reproducible.

¿Cómo Funciona?


El RabbitMQContainer utiliza Docker Compose para definir y gestionar el contenedor RabbitMQ. Al inicializar una instancia de esta clase, se genera un archivo docker-compose.yml temporal con la configuración necesaria para ejecutar el contenedor. Luego, el contenedor se inicia y se detiene automáticamente al final de la ejecución de las pruebas.

El proceso general de funcionamiento es el siguiente:

  1. Configuración del Contenedor: El método Build() define la configuración específica del contenedor RabbitMQ, incluyendo la ruta al archivo docker-compose.yml, opciones de reinicio forzado, eliminación de contenedores huérfanos, parada al finalizar y un nombre de servicio alternativo generado dinámicamente para evitar conflictos.
  2. Creación del Contenedor: Se crea una instancia de DockerComposeCompositeService usando la configuración definida.
  3. Inicio y Detención del Contenedor: El contenedor se inicia cuando se crea una instancia de la clase RabbitMQContainer y se detiene automáticamente cuando la instancia se elimina, asegurando la limpieza del entorno.

Container


El RabbitMQContainer hereda de la clase DockerCompose, la cual proporciona la lógica base para interactuar con Docker Compose. La clase utiliza la clase DockerComposeCompositeService de la librería Testcontainers para iniciar y detener contenedores docker. La configuración del contenedor se define en el método Build() que crea un archivo docker-compose.yml con los parámetros necesarios para la ejecución del contenedor RabbitMQ. El archivo docker-compose.yml se encuentra dentro del directorio Containers/RabbitMQContainer.

namespace CodeDesignPlus.Net.xUnit.Containers.RabbitMQContainer;
/// <summary>
/// Represents a Docker container for RabbitMQ, managed using Docker Compose.
/// </summary>
public class RabbitMQContainer : DockerCompose
{
/// <summary>
/// Builds the Docker Compose service configuration for the RabbitMQ container.
/// </summary>
/// <returns>An <see cref="ICompositeService"/> representing the Docker Compose service.</returns>
protected override ICompositeService Build()
{
// Define the path to the Docker Compose file.
var file = Path.Combine(Directory.GetCurrentDirectory(), "Containers", "RabbitMQContainer", "docker-compose.yml");
// Configure the Docker Compose settings.
var dockerCompose = new DockerComposeConfig
{
ComposeFilePath = new[] { file },
ForceRecreate = true,
RemoveOrphans = true,
StopOnDispose = true,
AlternativeServiceName = "rabbitmq_" + Guid.NewGuid().ToString("N"),
};
// Enable port retrieval and set the internal port and container name.
this.EnableGetPort = true;
this.InternalPort = 5672;
this.ContainerName = $"{dockerCompose.AlternativeServiceName}-rabbitmq";
// Create and return the Docker Compose service.
var compose = new DockerComposeCompositeService(base.DockerHost, dockerCompose);
return compose;
}
}

La clase RabbitMQCollectionFixture se utiliza como un fixture de xUnit, que proporciona una forma de compartir el contenedor entre las pruebas de una colección.

namespace CodeDesignPlus.Net.xUnit.Containers.RabbitMQContainer;
/// <summary>
/// Provides a fixture for managing a RabbitMQ container during xUnit tests.
/// </summary>
public sealed class RabbitMQCollectionFixture : IDisposable
{
/// <summary>
/// The name of the collection for the RabbitMQ tests.
/// </summary>
public const string Collection = "RabbitMQ Collection";
/// <summary>
/// Gets the RabbitMQ container instance.
/// </summary>
public RabbitMQContainer Container { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RabbitMQCollectionFixture"/> class.
/// </summary>
public RabbitMQCollectionFixture()
{
this.Container = new RabbitMQContainer();
// Wait for the RabbitMQ container to be fully initialized.
Thread.Sleep(10000);
}
/// <summary>
/// Disposes the RabbitMQ container instance.
/// </summary>
public void Dispose()
{
this.Container.StopInstance();
}
}

Ejemplo de Uso


Este ejemplo demuestra cómo usar RabbitMQContainer para realizar pruebas de integración con RabbitMQ. La prueba verifica la conexión a una instancia de RabbitMQ usando la información de conexión proporcionada por el contenedor, y asegura que la conexión esté abierta. Este ejemplo ilustra cómo utilizar las credenciales del contenedor y la instancia de conexión para interectuar con RabbitMQ en un entorno de prueba.

using CodeDesignPlus.Net.xUnit.Containers.RabbitMQContainer;
using RabbitMQ.Client;
namespace CodeDesignPlus.Net.xUnit.Test;
[Collection(RabbitMQCollectionFixture.Collection)]
public class RabbitMQContainerTest(RabbitMQCollectionFixture rabbitMQCollectionFixture)
{
private readonly RabbitMQContainer container = rabbitMQCollectionFixture.Container;
[Fact]
public async Task CheckConnectionServer()
{
var host = "localhost";
var port = container.Port;
var factory = new ConnectionFactory
{
HostName = host,
Port = port,
UserName = "usr_codedesignplus",
Password = "Temporal1"
};
// Act
IConnection connection = null!;
do
{
try
{
connection = await factory.CreateConnectionAsync();
// Assert
Assert.True(connection.IsOpen, "Connection should be open.");
}
catch
{
await Task.Delay(1000);
}
} while (connection == null || !connection.IsOpen);
Assert.NotNull(connection);
Assert.True(container.IsRunning);
Assert.True(connection.IsOpen);
}
}

Conclusiones


  • El RabbitMQContainer facilita la creación de pruebas unitarias que requieren una instancia de RabbitMQ.
  • Utiliza Docker Compose para la gestión del contenedor, proporcionando una forma sencilla de definir la configuración.
  • La clase RabbitMQCollectionFixture permite compartir un contenedor entre todas las pruebas de una misma colección.
  • El uso del RabbitMQContainer mejora la reproducibilidad y el aislamiento de las pruebas unitarias.
  • El contenedor RabbitMQ se inicia automaticamente al comienzo de cada prueba y se detiene una vez termina, esto permite un ambiente de pruebas limpio.

Referencias Adicionales