Escribe pruebas con Testcontainers
Para probar la aplicación, necesitas una instancia de LocalStack en ejecución que emule los servicios S3 y SQS de AWS. Testcontainers levanta LocalStack en un contenedor Docker y @DynamicPropertySource lo conecta a Spring Cloud AWS.
Configurar el contenedor de pruebas
Puedes iniciar un contenedor de LocalStack y configurar las propiedades de Spring Cloud AWS para comunicarse con él en lugar de los servicios reales de AWS. Las propiedades que debes configurar son:
spring.cloud.aws.s3.endpoint=http://localhost:4566
spring.cloud.aws.sqs.endpoint=http://localhost:4566
spring.cloud.aws.credentials.access-key=noop
spring.cloud.aws.credentials.secret-key=noop
spring.cloud.aws.region.static=us-east-1Para las pruebas, utiliza un contenedor efímero que se inicie en un puerto aleatorio disponible para que puedas ejecutar múltiples compilaciones en CI en paralelo sin conflictos de puertos.
Escribir la prueba
Crea MessageListenerTest.java:
package com.testcontainers.demo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS;
import java.io.IOException;
import java.time.Duration;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
@SpringBootTest
@Testcontainers
class MessageListenerTest {
@Container
static LocalStackContainer localStack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:3.0")
);
static final String BUCKET_NAME = UUID.randomUUID().toString();
static final String QUEUE_NAME = UUID.randomUUID().toString();
@DynamicPropertySource
static void overrideProperties(DynamicPropertyRegistry registry) {
registry.add("app.bucket", () -> BUCKET_NAME);
registry.add("app.queue", () -> QUEUE_NAME);
registry.add(
"spring.cloud.aws.region.static",
() -> localStack.getRegion()
);
registry.add(
"spring.cloud.aws.credentials.access-key",
() -> localStack.getAccessKey()
);
registry.add(
"spring.cloud.aws.credentials.secret-key",
() -> localStack.getSecretKey()
);
registry.add(
"spring.cloud.aws.s3.endpoint",
() -> localStack.getEndpointOverride(S3).toString()
);
registry.add(
"spring.cloud.aws.sqs.endpoint",
() -> localStack.getEndpointOverride(SQS).toString()
);
}
@BeforeAll
static void beforeAll() throws IOException, InterruptedException {
localStack.execInContainer("awslocal", "s3", "mb", "s3://" + BUCKET_NAME);
localStack.execInContainer(
"awslocal",
"sqs",
"create-queue",
"--queue-name",
QUEUE_NAME
);
}
@Autowired
StorageService storageService;
@Autowired
MessageSender publisher;
@Autowired
ApplicationProperties properties;
@Test
void shouldHandleMessageSuccessfully() {
Message message = new Message(UUID.randomUUID(), "Hello World");
publisher.publish(properties.queue(), message);
await()
.pollInterval(Duration.ofSeconds(2))
.atMost(Duration.ofSeconds(10))
.ignoreExceptions()
.untilAsserted(() -> {
String msg = storageService.downloadAsString(
properties.bucket(),
message.uuid().toString()
);
assertThat(msg).isEqualTo("Hello World");
});
}
}Esto es lo que hace la prueba:
@SpringBootTestinicia el contexto completo de la aplicación Spring.- Las anotaciones
@Testcontainersy@Containerde JUnit 5 de Testcontainers gestionan el ciclo de vida de la instancia deLocalStackContainer. @DynamicPropertySourceobtiene las URLs dinámicas de los endpoints de S3 y SQS, la región, la clave de acceso (access key) y la clave secreta (secret key) del contenedor, y las registra como propiedades de configuración de Spring Cloud AWS.@BeforeAllcrea la cola SQS y el bucket S3 requeridos usando la herramienta de CLIawslocalque viene preinstalada en la imagen Docker de LocalStack. La APIlocalStack.execInContainer()ejecuta comandos dentro del contenedor.shouldHandleMessageSuccessfully()publica unMessageen la cola SQS. El listener recibe el mensaje y almacena su contenido en el bucket S3 con el UUID como clave. Awaitility espera hasta 10 segundos para que aparezca el contenido esperado en el bucket.