# Escribir pruebas con Testcontainers


## Quarkus Dev Services

Quarkus Dev Services aprovisiona automáticamente servicios no configurados en modo
de desarrollo y prueba. Cuando incluyes una extensión y no la configuras, Quarkus
inicia el servicio correspondiente utilizando [Testcontainers](https://www.testcontainers.org/) en
segundo plano y conecta la aplicación para que utilice ese servicio.

> [!NOTE]
> Dev Services requiere un
> [entorno Docker compatible](https://www.testcontainers.org/supported_docker_environment/).

Quarkus Dev Services es compatible con la mayoría de los servicios de uso común,
como bases de datos SQL, Kafka, RabbitMQ, Redis y MongoDB. Para obtener más
información, consulta la
[guía de Quarkus Dev Services](https://quarkus.io/guides/dev-services) (en inglés).

## Escribir pruebas para los endpoints de la API

Prueba los endpoints `GET /api/customers` y `POST /api/customers` utilizando
REST Assured. La biblioteca `io.rest-assured:rest-assured` ya se agregó como
dependencia de prueba al generar el proyecto.

Crea `CustomerResourceTest.java` y anótalo con `@QuarkusTest`. Esto arranca la
aplicación junto con los servicios requeridos utilizando Dev Services. Dado que no
has configurado las propiedades del datasource, Dev Services inicia automáticamente
una base de datos PostgreSQL utilizando Testcontainers.

```java
package com.testcontainers.demo;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.common.mapper.TypeRef;
import io.restassured.http.ContentType;
import java.util.List;
import org.junit.jupiter.api.Test;

@QuarkusTest
class CustomerResourceTest {

    @Test
    void shouldGetAllCustomers() {
        List<Customer> customers = given().when()
                .get("/api/customers")
                .then()
                .statusCode(200)
                .extract()
                .as(new TypeRef<>() {});
        assertFalse(customers.isEmpty());
    }

    @Test
    void shouldCreateCustomerSuccessfully() {
        Customer customer = new Customer(null, "John", "john@gmail.com");
        given().contentType(ContentType.JSON)
                .body(customer)
                .when()
                .post("/api/customers")
                .then()
                .statusCode(201)
                .body("name", is("John"))
                .body("email", is("john@gmail.com"));
    }
}
```

Esto es lo que hace la prueba:

- `@QuarkusTest` inicia la aplicación completa de Quarkus con Dev Services habilitado.
- Dev Services inicia un contenedor PostgreSQL utilizando Testcontainers y configura
  el datasource automáticamente.
- `shouldGetAllCustomers()` llama a `GET /api/customers` y verifica que se devuelvan
  los datos sembrados por la migración de Flyway.
- `shouldCreateCustomerSuccessfully()` envía una solicitud `POST /api/customers` y
  verifica que la respuesta contenga los datos del cliente creado.

## Personalizar la configuración de las pruebas

De forma predeterminada, la instancia de prueba de Quarkus se inicia en el puerto
8081 y utiliza una imagen Docker de `postgres:14`. Personaliza ambos agregando
estas propiedades a `src/main/resources/application.properties`:

```properties
quarkus.http.test-port=0
quarkus.datasource.devservices.image-name=postgres:15.2-alpine
```

Establecer `quarkus.http.test-port=0` inicia la aplicación en un puerto aleatorio
disponible, evitando conflictos de puertos. La propiedad `devservices.image-name`
te permite fijar la imagen de PostgreSQL a una versión específica que coincida con
la de producción.

## Probar con servicios no compatibles con Dev Services

Es posible que tu aplicación utilice un servicio que Dev Services no admita de forma
nativa. En ese caso, utiliza `QuarkusTestResourceLifecycleManager` para iniciar el
servicio antes de que la aplicación Quarkus comience a ejecutarse para las pruebas.

Por ejemplo, supón que la aplicación utiliza CockroachDB. Primero, agrega la
dependencia del módulo CockroachDB de Testcontainers:

```xml
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>cockroachdb</artifactId>
    <scope>test</scope>
</dependency>
```

Crea un `CockroachDBTestResource` que implemente
`QuarkusTestResourceLifecycleManager`:

```java
package com.testcontainers.demo;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import java.util.HashMap;
import java.util.Map;
import org.testcontainers.containers.CockroachContainer;

public class CockroachDBTestResource implements QuarkusTestResourceLifecycleManager {

    CockroachContainer cockroachdb;

    @Override
    public Map<String, String> start() {
        cockroachdb = new CockroachContainer("cockroachdb/cockroach:v22.2.0");
        cockroachdb.start();
        Map<String, String> conf = new HashMap<>();
        conf.put("quarkus.datasource.jdbc.url", cockroachdb.getJdbcUrl());
        conf.put("quarkus.datasource.username", cockroachdb.getUsername());
        conf.put("quarkus.datasource.password", cockroachdb.getPassword());
        return conf;
    }

    @Override
    public void stop() {
        cockroachdb.stop();
    }
}
```

Utiliza el `CockroachDBTestResource` con `@QuarkusTestResource` en una clase de
prueba:

```java
package com.testcontainers.demo;

import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertFalse;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.common.mapper.TypeRef;
import java.util.List;
import org.junit.jupiter.api.Test;

@QuarkusTest
@QuarkusTestResource(value = CockroachDBTestResource.class, restrictToAnnotatedClass = true)
class CockroachDBTest {

    @Test
    void shouldGetAllCustomers() {
        List<Customer> customers = given().when()
                .get("/api/customers")
                .then()
                .statusCode(200)
                .extract()
                .as(new TypeRef<>() {});
        assertFalse(customers.isEmpty());
    }
}
```

El atributo `restrictToAnnotatedClass = true` garantiza que el contenedor de
CockroachDB solo se inicie cuando se ejecute esta clase de prueba específica, en
lugar de activarse para todas las pruebas.

