Skip to content

Redis

El RedisContainer es un componente de la librería CodeDesignPlus.Net.xUnit que proporciona un contenedor Docker para ejecutar Redis, un almacén de datos en memoria. Este componente facilita la creación de pruebas unitarias que dependen de Redis, ofreciendo un entorno de pruebas aislado y reproducible. Además, el componente permite probar diferentes configuraciones, incluyendo el uso de certificados y la configuración en modo cluster.

¿Cómo Funciona?


El RedisContainer utiliza Docker Compose para definir y gestionar el contenedor Redis. 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 Redis, 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 RedisContainer y se detiene automáticamente cuando la instancia se elimina, asegurando la limpieza del entorno.
  4. Inicialización de Servicios Redis: La clase inicializa dos servicios RedisService:
    • RedisServer: Configurado con un certificado y una contraseña PFX.
    • RedisServerWithoutPfxPassword: Configurado con un certificado pero sin contraseña PFX.
  5. Generación de Opciones de Configuración: La clase proporciona métodos para crear opciones de configuración para el servicio RedisService, facilitando la personalización de las pruebas.

Container


El RedisContainer 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 Redis. El archivo docker-compose.yml se encuentra dentro del directorio Containers/RedisContainer.

using CodeDesignPlus.Net.Redis.Abstractions;
using CodeDesignPlus.Net.Redis.Options;
using CodeDesignPlus.Net.Redis.Services;
namespace CodeDesignPlus.Net.xUnit.Containers.RedisContainer;
/// <summary>
/// Represents a Docker container for Redis, managed using Docker Compose.
/// </summary>
public class RedisContainer : DockerCompose
{
/// <summary>
/// Gets the mock logger for the Redis service.
/// </summary>
public Mock<ILogger<RedisService>> Logger { get; private set; } = new();
/// <summary>
/// Gets the Redis service instance with a PFX password.
/// </summary>
public RedisService RedisServer { get; private set; }
/// <summary>
/// Gets the Redis service instance without a PFX password.
/// </summary>
public RedisService RedisServerWithoutPfxPassword { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="RedisContainer"/> class.
/// </summary>
public RedisContainer()
{
RedisServerWithoutPfxPassword = StartSecondRedisServer();
RedisServer = StartFirstRedisServer();
}
/// <summary>
/// Starts the first Redis server with a PFX password.
/// </summary>
/// <returns>A configured <see cref="RedisService"/> instance.</returns>
private RedisService StartFirstRedisServer()
{
var options = CreateOptions("client.pfx", "Temporal1");
var redisService = new RedisService(Logger.Object);
redisService.Initialize(options.Value.Instances["test"]);
return redisService;
}
/// <summary>
/// Starts the second Redis server without a PFX password.
/// </summary>
/// <returns>A configured <see cref="RedisService"/> instance.</returns>
private RedisService StartSecondRedisServer()
{
var options = CreateOptions("client-without-pass.pfx");
var redisService = new RedisService(Logger.Object);
redisService.Initialize(options.Value.Instances["test"]);
return redisService;
}
/// <summary>
/// Builds the Docker Compose service configuration for the Redis container.
/// </summary>
/// <returns>An <see cref="ICompositeService"/> representing the Docker Compose service.</returns>
protected override ICompositeService Build()
{
var file = Path.Combine(Directory.GetCurrentDirectory(), "Containers", "RedisContainer", (TemplateString)"docker-compose.standalone.yml");
var dockerCompose = new DockerComposeConfig
{
ComposeFilePath = [file],
ForceRecreate = true,
RemoveOrphans = true,
StopOnDispose = true,
AlternativeServiceName = "redis_" + Guid.NewGuid().ToString("N"),
};
EnableGetPort = true;
InternalPort = 6380;
ContainerName = $"{dockerCompose.AlternativeServiceName}-redis";
var compose = new DockerComposeCompositeService(DockerHost, dockerCompose);
return compose;
}
/// <summary>
/// Creates Redis options for the specified certificate and password.
/// </summary>
/// <param name="certificate">The certificate file name.</param>
/// <param name="password">The password for the certificate. Default is null.</param>
/// <param name="instanceName">The name of the Redis instance. Default is "test".</param>
/// <returns>A configured <see cref="RedisOptions"/> instance.</returns>
public RedisOptions RedisOptions(string certificate, string password = null, string instanceName = "test")
{
var pfx = Path.Combine(Directory.GetCurrentDirectory(), "Containers", "RedisContainer", "Certificates", certificate);
if (!File.Exists(pfx))
throw new InvalidOperationException("Can't run unit test because certificate does not exist");
var instance = new Instance()
{
ConnectionString = $"localhost:{Port},ssl=true,password=12345678,resolveDns=false,sslprotocols=tls12|tls13",
Certificate = pfx,
PasswordCertificate = password,
};
if (!string.IsNullOrEmpty(password))
instance.PasswordCertificate = password;
var redisOptions = new RedisOptions();
redisOptions.Instances.Add(instanceName, instance);
return redisOptions;
}
/// <summary>
/// Creates options for the Redis service.
/// </summary>
/// <param name="certificate">The certificate file name.</param>
/// <param name="password">The password for the certificate. Default is null.</param>
/// <returns>A configured <see cref="IOptions{RedisOptions}"/> instance.</returns>
private IOptions<RedisOptions> CreateOptions(string certificate, string password = null)
{
return Options.Create(RedisOptions(certificate, password));
}
}

La clase RedisContainer utiliza la clase RedisService para realizar las operaciones de conexión con redis, y configurar las opciones para dicha conexión, se tiene la opción de configurar con certificado y sin certificado.

Ejemplo de Uso


Este ejemplo ilustra cómo usar RedisContainer para realizar pruebas de integración con Redis. La prueba verifica la conexión a una instancia de Redis utilizando el servicio RedisServer proporcionado por el contenedor y asegura que la conexión esté activa. Este ejemplo muestra el uso de la clase RedisService y su propiedad IsConnected para validar una conexión exitosa a Redis en un contexto de prueba.

using CodeDesignPlus.Net.xUnit.Containers.RedisContainer;
namespace CodeDesignPlus.Net.xUnit.Test;
[Collection(RedisCollectionFixture.Collection)]
public class RedisContainerTest(RedisCollectionFixture fixture)
{
private readonly RedisContainer redisContainer = fixture.Container;
[Fact]
public void CheckConnectionServer()
{
var server = this.redisContainer.RedisServer;
Assert.True(server.IsConnected);
}
}

Conclusiones


  • El RedisContainer facilita la creación de pruebas unitarias que requieren una instancia de Redis.
  • Utiliza Docker Compose para la gestión del contenedor, proporcionando una forma sencilla de definir la configuración.
  • Proporciona servicios RedisService con diferentes configuraciones de certificados para la conexión.
  • El uso del RedisContainer mejora la reproducibilidad y el aislamiento de las pruebas unitarias.
  • El contenedor Redis se inicia automaticamente al comienzo de cada prueba y se detiene una vez termina, esto permite un ambiente de pruebas limpio.

Referencias Adicionales