Teses de integración sobre mocks de un OpenAPI con Microcks

Escrito por picodotdev el .
java planeta-codigo software-libre
Enlace permanente Comentarios

Los tests unitarios son rápidos, pero se ejecutan en un entorno mínimo y asilado que no es el de producción. Con Testcontainers, los tests de integración pueden usar las mismas herramientas que en producción, como PostgreSQL o Kafka. En este artículo vemos cómo probar los clientes REST de un microservicio, es decir, los adaptadores de salida de una arquitectura hexagonal, con Microcks. Microcks es un servidor mock de la CNCF que genera las respuestas directamente a partir de la definición OpenAPI o AsyncAPI de un servicio, y que permite empezar a desarrollar sin tener el servicio real ni un entorno sandbox.

Microcks

Los teses unitarios son los más numerosos por su rapidez de ejecución y probar el objeto bajo prueba de forma aislada y controlada. Sin embargo, los teses unitarios se ejecutan en un entorno controlado sintético que no refleja el entorno de ejecución real. Los teses de integración prueban el objeto bajo prueba con elementos adicionales que representa de forma más fiable el entorno en el que se ejecutan en el entorno de producción.

Los teses de integración han sido a veces limitados por la dificultad de simular el entorno de producción. Ahora gracias a los contenedores y con Testcontainers es posible crear teses de integración que usen las mismas herramientas de producción como una base de datos relacional PostgresSQL o un sistema de mensajería como Apache Kafka.

Con Testcontainers es posible probar de forma completa el código de la aplicación incluida esa parte que se relaciona con el sistema externo. Por ejemplo, probar el acceso a una base de datos relacional y lanzar SQLs o enviar y recibir mensajes de Kafa y Spring Cloud Stream.

Una parte habitual en los microservicios es probar las peticiones REST que realiza a otros microservicios. Para esto se necesita un servidor ficticio o mock con respuestas preprogramadas que simulen las respuestas y que permitan validar el código de infraestructura y adaptador de salida en una arquitectura hexagonal que realiza las peticiones al sistema externo.

Mickrocks

Microcks es un servidor adoptado en la CNCF que permite preprogramar respuestas de servicios REST y sistemas de mensajes proporcionando simplemente la definición del documento OpenAPI o AsynAPI. A partir del documento OpenAPI para cada endpoint de la API se programa una respuesta válida según la definición de la API.

Hay varias formas de usar Mickrocks, una de ellas es en teses de integración con Testcontainers y Spring Boot para probar un cliente de una API. Esto tiene la ventaja de que teniendo disponible la definición del OpenAPI es posible empezar a desarrollar el cliente sin necesidad de tener la parte servidora real o un entorno sandbox para el mismo.

Otra forma de ejecución es mediante un contenedor que arranque el servidor de Microcks. El propio servidor permite cargar varios ejemplos de APIs con su definición de OpenAPI como los de petstore y pastry.

Microcks adicionalmente permite probar API en GraphQL, gRPC y los mencionados AsyncAPI. Otras opciones solo soportan HTTP preprogramando las peticiones y respuestas a más bajo nivel sin partir del documento OpenAPI.

Contenedor de Microcks

En la guía de inicio de Microcks se muestra y explica como probar Microcks utilizando Docker.

Lo primero es iniciar el contenedor de Microscks.

1
2
docker run -p 8585:8080 -it --rm quay.io/microcks/microcks-uber:latest-native

docker-run.sh

Accediendo a http://localhost:8585 con el navegador se carga al panel de administración.

Consola de Microcks

Consola de Microcks

Desde Microcks Hub es posible cargar un ejemplo de OpenAPI.

Ejemplos de APIs en el Hub de Microcks Ejemplos de APIs en el Hub de Microcks Ejemplos de APIs en el Hub de Microcks

Ejemplos de APIs en el Hub de Microcks

Ejemplos de APIs en el Hub de Microcks

En APIs | Services es posible inspeccionar las APIs cargadas como mocks.

Ejemplo de mock de un OpenAPI en Microcks Ejemplo de mock de un OpenAPI en Microcks Ejemplo de mock de un OpenAPI en Microcks

Ejemplo de mock de un OpenAPI en Microcks

Con el comando curl se realiza la petición REST a la API mockeada.

1
2
curl -X GET 'http://localhost:8585/rest/API+Pastry+-+2.0/2.0.0/pastry/Eclair+Cafe' -H 'Accept: application/json'

curl-pastry-request.sh

Este es la respuesta.

1
2
{"name":"Eclair Cafe","description":"Delicieux Eclair au Cafe pas calorique du tout","size":"M","price":2.5,"status":"available"}

pastry-response.out

Test de integración con Testcontainers y Spring Boot

Una forma alternativa muy útil a un servidor autónomo es realizar las pruebas como teses de integración del servicio. En la definición del contenedor de Testcontainers se añade el OpenAPI para que Microcks cree el mock, el proprio OpenAPI tiene instrucciones para definir el compoeramiento del mock con ejemplos de valores y código para su comportamiento. Tras crear el contenedor es posible obtener la URL base del servidor al que añadir el path del endpoint al que enviar las peticiones.

Utilizando RestClient el siguinte código hace una petición al servicio REST desde un test de integración que arranca Microcks con Testcontainers y obtiene el base URL del servidor de Mickrocks. La propiedad de configuración pastry.api.base-url se configura dinámicamente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package io.github.picodotdev.blogbitix.microcks;

import static org.assertj.core.api.Assertions.assertThat;

import java.math.BigDecimal;

import io.github.microcks.testcontainers.MicrocksContainer;
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.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@SpringBootTest
@Testcontainers
class PastryClientTest {

    @Container
    static MicrocksContainer microcks = new MicrocksContainer(DockerImageName.parse("quay.io/microcks/microcks-uber:1.11.0"))
                                                .withMainArtifacts("pastry-openapi.yaml");

    @DynamicPropertySource
    static void apiProperties(DynamicPropertyRegistry registry) {
        registry.add("pastry.api.base-url", () -> microcks.getRestMockEndpoint("API Pastries", "0.0.1"));
    }

    @Autowired
    PastryClient client;

    @Test
    void shouldFindPastryByName() {
        Pastry pastry = client.findByName("Millefeuille");

        assertThat(pastry.name()).isEqualTo("Millefeuille");
        assertThat(pastry.size()).isEqualTo("L");
        assertThat(pastry.price()).isEqualByComparingTo(new BigDecimal("4.40"));
        assertThat(pastry.status()).isEqualTo("available");
    }
}
PastryClientTest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package io.github.picodotdev.blogbitix.microcks;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

@Component
public class PastryClient {

    private final RestClient restClient;

    public PastryClient(RestClient.Builder builder, @Value("${pastry.api.base-url}") String baseUrl) {
        this.restClient = builder.baseUrl(baseUrl).build();
    }

    public Pastry findByName(String name) {
        return restClient.get()
                         .uri("/pastries/{name}", name)
                         .retrieve()
                         .body(Pastry.class);
    }
}
PastryClient.java
1
2
3
pastry:
  api:
    base-url: https://pastry.example.com/api
application.yml
1
2
3
4
5
6
package io.github.picodotdev.blogbitix.microcks;

import java.math.BigDecimal;

public record Pastry(String name, String description, String size, BigDecimal price, String status) {
}
Pastry.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
plugins {
    application
    id("org.springframework.boot") version "4.1.2"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:4.1.2"))

    implementation("org.springframework.boot:spring-boot-starter-webmvc")
    implementation("org.springframework.boot:spring-boot-starter-restclient")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.testcontainers:testcontainers-junit-jupiter")
    testImplementation("io.github.microcks:microcks-testcontainers:0.5.0")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

application {
    mainClass = "io.github.picodotdev.blogbitix.microcks.Main"
}

tasks.withType<Test> {
    useJUnitPlatform()
}
build.gradle.kts

Otras opciones

Otras opciones populares de servidores mock para peticiones REST son WireMock, Mock Server y Prism. Sin embargo, estas opciones solo soportan el protocolo REST y no soportan el resto de protocolos que soporta Microcks. Otro aspecto relevante es que se programan con peticiones de ejemplo y no a partir de la definición del OpenAPI.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew test


Comparte el artículo: