SpringBoot
Tutorial SpringBoot: Testing de aplicaciones reactivas WebFlux
Utiliza WebTestClient y StepVerifier para desarrollar pruebas de software de aplicaciones reactivas de Spring WebFlux en Spring Boot, maneja la reactividad de forma robusta.
Aprende SpringBoot GRATIS y certifícatereactor-test en Spring WebFlux
El módulo reactor-test proporciona un conjunto de herramientas esenciales para realizar pruebas unitarias en aplicaciones reactivas con Spring WebFlux. Estas herramientas facilitan la simulación y verificación de flujos reactivos, permitiendo asegurar el correcto funcionamiento de Mono y Flux en diversos escenarios.
Una de las principales utilidades es el StepVerifier, que permite definir expectativas sobre las emisiones y eventos de un flujo reactivo. Con StepVerifier, se pueden crear pruebas detalladas que verifiquen secuencias de datos, errores y completitud de los flujos.
Flux<String> flujo = Flux.just("Alpha", "Beta", "Gamma");
StepVerifier.create(flujo)
.expectNext("Alpha")
.expectNext("Beta")
.expectNext("Gamma")
.verifyComplete();
Para pruebas que involucran operaciones temporizadas, el VirtualTimeScheduler es indispensable. Esta herramienta permite manipular el tiempo virtualmente, acelerando la ejecución de flujos que dependen de retrasos o intervalos sin afectar el tiempo real de ejecución de las pruebas.
import reactor.test.scheduler.VirtualTimeScheduler;
VirtualTimeScheduler virtualTimeScheduler = VirtualTimeScheduler.getOrSet();
Flux<Long> flujoIntervalo = Flux.interval(Duration.ofSeconds(5))
.take(2);
StepVerifier.withVirtualTime(() -> flujoIntervalo, () -> virtualTimeScheduler)
.thenAwait(Duration.ofSeconds(10))
.expectNext(0L, 1L)
.verifyComplete();
El TestPublisher es una herramienta flexible para crear publishers controlables durante las pruebas. Con TestPublisher, es posible emitir señales manualmente, ya sea datos, errores o eventos de completitud, facilitando la simulación de diferentes condiciones en los flujos.
import reactor.test.publisher.TestPublisher;
TestPublisher<String> testPublisher = TestPublisher.create();
Flux<String> flujoPrueba = testPublisher.flux();
StepVerifier.create(flujoPrueba)
.then(() -> testPublisher
.next("Dato1", "Dato2")
.error(new RuntimeException("Error simulado")))
.expectNext("Dato1", "Dato2")
.expectErrorMatches(throwable ->
throwable instanceof RuntimeException &&
throwable.getMessage().equals("Error simulado"))
.verify();
Además, reactor-test ofrece herramientas como Scannable para inspeccionar internamente los operadores de un flujo, y utilidades para trabajar con Context y verificar la propagación de datos contextuales a través de los flujos reactivos.
Otra característica útil es la capacidad de utilizar Hooks para interceptar y modificar el comportamiento global de los flujos durante las pruebas. Con los Hooks, es posible inyectar comportamientos personalizados o captar métricas detalladas de ejecución.
Hooks.onOperatorDebug();
Mono<String> flujoConError = Mono.error(new IllegalArgumentException("Argumento no válido"));
StepVerifier.create(flujoConError)
.verifyErrorMessage("Argumento no válido");
Todas estas herramientas se integran de forma natural con frameworks de pruebas como JUnit 5, permitiendo escribir pruebas claras y expresivas para aplicaciones reactivas. Al aprovechar las utilidades de reactor-test, los desarrolladores pueden garantizar que sus aplicaciones reactivas respondan correctamente bajo diversas condiciones y manejos de datos.
Testing de operadores y flujos complejos con StepVerifier, operador test
Cuando trabajamos con aplicaciones reactivas en Spring WebFlux, es fundamental asegurar que los flujos complejos y los operadores se comporten según lo esperado. El uso de StepVerifier nos permite crear pruebas detalladas y precisas para validar estos flujos.
El StepVerifier es una herramienta que facilita la verificación paso a paso de las emisiones y eventos en flujos Mono y Flux. Al enfrentarnos a flujos que incorporan operadores como flatMap, zip, merge o concat, es esencial comprobar que la combinación y transformación de datos se realiza correctamente.
Consideremos un ejemplo donde utilizamos el operador flatMap para transformar y combinar datos de múltiples fuentes:
Flux<String> nombres = Flux.just("Ana", "Juan", "Pedro");
Flux<Integer> edades = Flux.just(25, 30, 35);
Flux<String> combinacion = nombres
.flatMap(nombre -> edades
.map(edad -> nombre + " tiene " + edad + " años")
);
StepVerifier.create(combinacion)
.expectNext("Ana tiene 25 años", "Ana tiene 30 años", "Ana tiene 35 años",
"Juan tiene 25 años", "Juan tiene 30 años", "Juan tiene 35 años",
"Pedro tiene 25 años", "Pedro tiene 30 años", "Pedro tiene 35 años")
.verifyComplete();
En este ejemplo, el uso de expectNext nos permite especificar de manera precisa la secuencia esperada de valores. Es importante notar que, debido a la naturaleza combinatoria de flatMap, el orden de emisión puede variar. Para manejar casos donde el orden no es determinante, podemos utilizar expectNextMatches o recordWith junto con consumeRecordedWith para validar los elementos de forma más flexible.
Cuando trabajamos con operadores temporales como delayElements o interval, la gestión del tiempo en las pruebas se vuelve crucial. Aquí es donde el Operador de Prueba y el VirtualTimeScheduler son de gran utilidad. El VirtualTimeScheduler nos permite simular el paso del tiempo sin demoras reales en la ejecución de las pruebas.
import reactor.test.scheduler.VirtualTimeScheduler;
VirtualTimeScheduler.getOrSet();
Flux<Long> flujoIntervalo = Flux.interval(Duration.ofSeconds(5))
.take(3);
StepVerifier.withVirtualTime(() -> flujoIntervalo)
.expectSubscription()
.thenAwait(Duration.ofSeconds(15))
.expectNext(0L, 1L, 2L)
.verifyComplete();
En este caso, utilizamos withVirtualTime para indicar que el flujo debe ejecutarse en un contexto de tiempo virtual. Al thenAwait por la duración necesaria, podemos avanzar el tiempo y validar las emisiones esperadas rápidamente.
Para flujos que pueden generar errores, es fundamental probar tanto el camino exitoso como el manejo de excepciones. Supongamos un flujo que procesa una lista de números y arroja una excepción si encuentra un valor negativo:
Flux<Integer> numeros = Flux.just(1, 2, -3, 4);
Flux<Integer> procesados = numeros.map(numero -> {
if (numero < 0) {
throw new IllegalArgumentException("Número negativo no permitido");
}
return numero * 2;
});
StepVerifier.create(procesados)
.expectNext(2, 4)
.expectErrorMatches(error -> error instanceof IllegalArgumentException &&
error.getMessage().equals("Número negativo no permitido"))
.verify();
Aquí, expectErrorMatches nos permite validar que el flujo efectivamente arroja la excepción esperada con el mensaje correcto, asegurando un manejo adecuado de errores.
Cuando los flujos son más complejos y combinan múltiples operadores, es posible que necesitemos utilizar StepVerifier.Assertions para realizar verificaciones más detalladas. Por ejemplo, al trabajar con flujos anidados o encadenados, podemos usar assertNext para inspeccionar cada elemento individualmente:
Flux<String> datos = Flux.just("A", "B", "C")
.concatWith(Flux.error(new RuntimeException("Error simulado")))
.onErrorReturn("D");
StepVerifier.create(datos)
.expectNext("A")
.expectNext("B")
.expectNext("C")
.expectNext("D")
.verifyComplete();
En este flujo, tras la ocurrencia de un error, utilizamos onErrorReturn para emitir un valor predeterminado. Con StepVerifier, podemos verificar que el flujo emite los valores adecuados incluso después de manejar un error.
En situaciones donde necesitamos comprobar aspectos más sutiles del flujo, como las solicitudes de demanda (backpressure), StepVerifier ofrece métodos como expectSubscription y thenRequest. Esto es útil para validar cómo el flujo responde a diferentes patrones de demanda:
Flux<Integer> numeros = Flux.range(1, 10);
StepVerifier.create(numeros, StepVerifierOptions.create().initialRequest(0))
.expectSubscription()
.thenRequest(5)
.expectNext(1, 2, 3, 4, 5)
.thenRequest(5)
.expectNext(6, 7, 8, 9, 10)
.verifyComplete();
Mediante el control explícito de las solicitudes, podemos simular consumidores lentos y verificar que el flujo se comporta correctamente bajo presión de demanda.
Es posible que necesitemos probar flujos que incluyan suscripciones condicionales o cancelaciones. Para estos casos, StepVerifier permite verificar eventos como la suscripción y cancelación mediante thenCancel y expectNoEvent:
Flux<Long> infinito = Flux.interval(Duration.ofMillis(100));
StepVerifier.create(infinito)
.expectSubscription()
.expectNext(0L, 1L, 2L)
.thenCancel()
.verify();
Con este enfoque, podemos asegurar que nuestros flujos reaccionan adecuadamente a las cancelaciones y no consumen recursos innecesarios.
El uso avanzado de StepVerifier es esencial para probar operadores y flujos complejos en Spring WebFlux. Al comprender y aplicar estas técnicas, garantizamos que nuestras aplicaciones reactivas funcionen de manera fiable y eficiente.
Validación de secuencias Mono y Flux
La validación de secuencias Mono y Flux es esencial para asegurar que las aplicaciones reactivas en Spring WebFlux se comporten según lo esperado. Al realizar pruebas unitarias de estas secuencias, es importante verificar no solo los datos emitidos, sino también cómo se gestionan los errores, las cancelaciones y las suscripciones.
Para validar un Mono, que representa una secuencia que puede emitir cero o un elemento, podemos utilizar el StepVerifier para comprobar el valor emitido o verificar que no se emite ningún valor. Además, es fundamental validar cómo se manejan las señales de éxito y error en la secuencia.
Por ejemplo, para probar un Mono que debería emitir un valor específico:
@Test
void testMonoConValor() {
Mono<String> mono = Mono.just("Hola Mundo");
StepVerifier.create(mono)
.expectNext("Hola Mundo")
.verifyComplete();
}
En este caso, el StepVerifier verifica que el Mono emite el valor "Hola Mundo" y luego completa correctamente.
Si queremos probar un Mono que no emite ningún valor pero completa sin errores:
@Test
void testMonoVacio() {
Mono<String> monoVacio = Mono.empty();
StepVerifier.create(monoVacio)
.verifyComplete();
}
Aquí, validamos que el Mono está vacío y completa satisfactoriamente.
Para el caso donde el Mono emite un error, es crucial verificar el tipo de excepción y el mensaje asociado:
@Test
void testMonoConError() {
Mono<String> monoError = Mono.error(new IllegalStateException("Estado no permitido"));
StepVerifier.create(monoError)
.expectErrorMatches(throwable ->
throwable instanceof IllegalStateException &&
throwable.getMessage().equals("Estado no permitido"))
.verify();
}
Con este enfoque, confirmamos que el Mono lanza una IllegalStateException con el mensaje esperado.
Al validar secuencias Flux, que pueden emitir múltiples elementos, es importante comprobar la secuencia completa de valores, así como el manejo de errores y la correcta finalización de la secuencia.
Por ejemplo, para probar un Flux que emite una serie de números:
@Test
void testFluxDeNumeros() {
Flux<Integer> numeros = Flux.range(1, 5);
StepVerifier.create(numeros)
.expectNext(1, 2, 3, 4, 5)
.verifyComplete();
}
En este test, aseguramos que el Flux emite los números del 1 al 5 y luego completa sin errores.
Si la secuencia incluye una mezcla de valores y errores, podemos validarla de la siguiente manera:
@Test
void testFluxConError() {
Flux<String> datos = Flux.just("A", "B", "C")
.concatWith(Mono.error(new RuntimeException("Error de prueba")));
StepVerifier.create(datos)
.expectNext("A")
.expectNext("B")
.expectNext("C")
.expectErrorMessage("Error de prueba")
.verify();
}
Aquí, verificamos que el Flux emite los valores "A", "B", "C" y luego termina con un error que contiene el mensaje "Error de prueba".
Para secuencias que pueden tardar cierto tiempo en emitir valores, como aquellas que utilizan operadores temporales, debemos manejar la validación del tiempo adecuadamente. El uso de StepVerifier junto con VirtualTimeScheduler permite simular el paso del tiempo en las pruebas:
@Test
void testFluxConRetardo() {
VirtualTimeScheduler.getOrSet();
Flux<Long> intervalo = Flux.interval(Duration.ofSeconds(1)).take(3);
StepVerifier.withVirtualTime(() -> intervalo)
.expectSubscription()
.thenAwait(Duration.ofSeconds(3))
.expectNext(0L, 1L, 2L)
.verifyComplete();
}
En este ejemplo, utilizamos el VirtualTimeScheduler para avanzar el tiempo virtualmente y verificar que el Flux emite los valores esperados sin tener que esperar realmente tres segundos.
Es común también validar transformaciones en las secuencias, asegurando que los operadores aplicados producen los resultados deseados. Por ejemplo, podemos probar un Flux que transforma cadenas a mayúsculas:
@Test
void testFluxTransformado() {
Flux<String> flux = Flux.just("spring", "boot", "reactivo")
.map(String::toUpperCase);
StepVerifier.create(flux)
.expectNext("SPRING", "BOOT", "REACTIVO")
.verifyComplete();
}
Aquí, validamos que la transformación a mayúsculas se realiza correctamente en cada elemento del Flux.
Cuando las secuencias son condicionales o dependen de ciertos criterios, podemos utilizar expectNextMatches para validar elementos que satisfacen una condición específica:
@Test
void testFluxConFiltro() {
Flux<Integer> numeros = Flux.range(1, 10)
.filter(n -> n % 2 == 0);
StepVerifier.create(numeros)
.expectNextMatches(n -> n == 2)
.expectNextMatches(n -> n == 4)
.expectNextMatches(n -> n == 6)
.expectNextMatches(n -> n == 8)
.expectNextMatches(n -> n == 10)
.verifyComplete();
}
En este caso, confirmamos que el Flux filtra correctamente los números pares del rango dado.
La validación del comportamiento bajo presión de demanda (backpressure) es otra consideración clave, especialmente en flujos donde el consumidor es más lento que el productor. Podemos simular y verificar este escenario en nuestras pruebas:
@Test
void testBackpressure() {
Flux<Integer> numeros = Flux.range(1, 10);
StepVerifier.create(numeros, StepVerifierOptions.create().initialRequest(2))
.expectNext(1, 2)
.thenRequest(2)
.expectNext(3, 4)
.thenRequest(6)
.expectNext(5, 6, 7, 8, 9, 10)
.verifyComplete();
}
Este test muestra cómo el Flux responde a las solicitudes de demanda, emitiendo solo los elementos solicitados en cada momento.
Es importante también validar los flujos que utilizan contextos reactivos, los cuales permiten pasar información a través de la secuencia sin modificar los datos emitidos. Podemos verificar que el contexto se propaga correctamente:
@Test
void testFluxConContexto() {
Mono<String> saludo = Mono.deferContextual(ctx ->
Mono.just("Hola " + ctx.get("usuario"))
);
StepVerifier.create(saludo.contextWrite(Context.of("usuario", "Carlos")))
.expectNext("Hola Carlos")
.verifyComplete();
}
En este ejemplo, verificamos que el Contexto proporciona el valor adecuado al operador y que el Mono resultante emite el saludo correcto.
Al probar secuencias que involucran suscripciones y cancelaciones, podemos utilizar métodos como thenCancel para asegurar que el flujo se comporta adecuadamente ante una cancelación:
@Test
void testFluxCancelado() {
Flux<Long> infinito = Flux.interval(Duration.ofMillis(100));
StepVerifier.create(infinito)
.expectNextCount(5)
.thenCancel()
.verify();
}
Este test confirma que después de emitir cinco elementos, el Flux puede ser cancelado sin problemas.
Finalmente, cuando trabajamos con flujos combinados o anidados, es esencial validar cómo interactúan entre sí. Por ejemplo, al combinar dos Flux con el operador zip:
@Test
void testFluxCombinado() {
Flux<String> nombres = Flux.just("Ana", "Luis", "María");
Flux<Integer> edades = Flux.just(30, 25, 28);
Flux<String> combinados = Flux.zip(nombres, edades, (nombre, edad) -> nombre + " tiene " + edad + " años");
StepVerifier.create(combinados)
.expectNext("Ana tiene 30 años")
.expectNext("Luis tiene 25 años")
.expectNext("María tiene 28 años")
.verifyComplete();
}
Con este test, aseguramos que el Flux resultante de la combinación emite los valores esperados en el orden correcto.
Uso de WebTestClient
El WebTestClient es una herramienta esencial para realizar pruebas integradas en aplicaciones reactivas desarrolladas con Spring WebFlux. Permite simular peticiones HTTP y verificar tanto las respuestas como el comportamiento de los controladores de forma asíncrona y no bloqueante.
Para comenzar a utilizar WebTestClient, es necesario configurarlo en nuestras pruebas. Una forma común es crear una instancia vinculada al servidor WebFlux de la aplicación:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControladorReactivoTest {
@Autowired
private WebTestClient webTestClient;
// Pruebas aquí
}
En este ejemplo, con la anotación @SpringBootTest, levantamos el contexto de la aplicación en un puerto aleatorio y autoinyectamos el WebTestClient. De este modo, podemos realizar pruebas que interactúan con el servidor real, abarcando todo el flujo desde la recepción de la petición hasta la generación de la respuesta.
Si preferimos probar solo el controlador específico sin levantar todo el contexto de la aplicación, podemos utilizar @WebFluxTest:
@WebFluxTest(ControladorReactivo.class)
public class ControladorReactivoTest {
@Autowired
private WebTestClient webTestClient;
// Pruebas aquí
}
Aquí, @WebFluxTest carga únicamente los componentes necesarios para probar el controlador ControladorReactivo.
A continuación, veamos cómo utilizar WebTestClient para realizar una petición GET y validar la respuesta:
@Test
void testObtenerElemento() {
webTestClient.get()
.uri("/api/elementos/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody(Elemento.class)
.value(elemento -> {
assertEquals(1L, elemento.getId());
assertEquals("Elemento 1", elemento.getNombre());
});
}
En esta prueba, realizamos una solicitud GET a /api/elementos/1, indicamos que aceptamos JSON y luego intercambiamos la solicitud. Validamos que el estatus HTTP es 200 OK, que el header Content-Type es application/json y que el cuerpo de la respuesta contiene un objeto Elemento con los valores esperados.
Para probar una solicitud POST, podemos enviar un objeto en el cuerpo de la petición y verificar la respuesta:
@Test
void testCrearElemento() {
Elemento nuevoElemento = new Elemento(null, "Nuevo Elemento");
webTestClient.post()
.uri("/api/elementos")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.bodyValue(nuevoElemento)
.exchange()
.expectStatus().isCreated()
.expectHeader().exists("Location")
.expectBody(Elemento.class)
.value(elemento -> {
assertNotNull(elemento.getId());
assertEquals("Nuevo Elemento", elemento.getNombre());
});
}
Aquí, enviamos un Elemento sin ID, indicando que es un nuevo recurso. Validamos que el estatus es 201 Created, que existe el header Location y que el cuerpo de la respuesta contiene el elemento creado con un ID asignado.
El WebTestClient también permite probar flujos reactivos como los que retornan un Flux. Por ejemplo, para probar un endpoint que devuelve una lista de elementos:
@Test
void testObtenerTodosLosElementos() {
webTestClient.get()
.uri("/api/elementos")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_NDJSON)
.expectBodyList(Elemento.class)
.value(elementos -> {
assertFalse(elementos.isEmpty());
assertTrue(elementos.size() >= 1);
});
}
En este caso, verificamos que el contenido es de tipo application/x-ndjson, propio de flujos Flux serializados en JSON. Validamos que la lista de elementos no esté vacía y que contenga al menos un elemento.
Para pruebas más avanzadas, podemos utilizar el método returnResult() para acceder al flujo reactivo y realizar validaciones más detalladas:
@Test
void testObtenerFlujoDeElementos() {
Flux<Elemento> flujoElementos = webTestClient.get()
.uri("/api/elementos/stream")
.accept(MediaType.APPLICATION_STREAM_JSON)
.exchange()
.expectStatus().isOk()
.returnResult(Elemento.class)
.getResponseBody();
StepVerifier.create(flujoElementos)
.expectNextMatches(elemento -> elemento.getNombre().startsWith("Elemento"))
.expectNextCount(4)
.thenCancel()
.verify();
}
En este ejemplo, obtenemos el Flux de elementos y utilizamos StepVerifier para validar el flujo. Comprobamos que el primer elemento cumple una condición específica y luego esperamos cuatro elementos más antes de cancelar el flujo.
El WebTestClient también es útil para probar manejo de errores en los endpoints. Si un endpoint retorna un error, podemos validar el estatus y el cuerpo de la respuesta:
@Test
void testElementoNoEncontrado() {
webTestClient.get()
.uri("/api/elementos/999")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isNotFound()
.expectBody()
.isEmpty();
}
Aquí, intentamos obtener un elemento con un ID inexistente y verificamos que el estatus es 404 Not Found y que el cuerpo de la respuesta está vacío.
Para probar endpoints protegidos con Spring Security, podemos configurar el WebTestClient para incluir autenticación. Por ejemplo:
@Test
void testAccesoNoAutorizado() {
webTestClient.get()
.uri("/api/elementos/1")
.exchange()
.expectStatus().isUnauthorized();
}
@Test
void testAccesoAutorizado() {
webTestClient.mutate().filter(basicAuthentication("user", "password")).build()
.get()
.uri("/api/elementos/1")
.exchange()
.expectStatus().isOk()
.expectBody(Elemento.class)
.value(elemento -> assertEquals("Elemento 1", elemento.getNombre()));
}
En la primera prueba, verificamos que sin credenciales, el acceso está no autorizado. En la segunda, añadimos autenticación básica y comprobamos que podemos acceder al recurso.
Además, el WebTestClient permite validar los cookies y las sesiones que se mantienen entre las peticiones:
@Test
void testManejoDeCookies() {
webTestClient.get()
.uri("/api/iniciar-sesion")
.exchange()
.expectStatus().isOk()
.expectCookie().exists("SESSIONID")
.expectBody()
.isEmpty();
}
Aquí, después de iniciar sesión, verificamos que se ha establecido una cookie de sesión llamada SESSIONID.
Cuando los endpoints retornan contenido binario o tipos de medios personalizados, podemos ajustar el WebTestClient para manejarlos adecuadamente:
@Test
void testDescargaDeArchivo() {
webTestClient.get()
.uri("/api/descargar/archivo")
.accept(MediaType.APPLICATION_OCTET_STREAM)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_OCTET_STREAM)
.expectBody(byte[].class)
.value(bytes -> assertTrue(bytes.length > 0));
}
En este caso, descargamos un archivo y verificamos que el cuerpo de la respuesta contiene datos.
El uso de WebTestClient también es aplicable para probar websockets y otros protocolos basados en HTTP. Sin embargo, en el contexto de REST APIs y aplicaciones WebFlux, su principal utilidad es permitir pruebas reactivas y no bloqueantes que reflejan el comportamiento real de la aplicación.
Es importante destacar que podemos personalizar el WebTestClient para añadir filtros, codificadores y otras configuraciones necesarias para nuestras pruebas:
WebTestClient clientPersonalizado = WebTestClient.bindToServer()
.baseUrl("http://localhost:8080")
.filter(logRequest())
.filter(logResponse())
.build();
Con este WebTestClient personalizado, añadimos filtros para registrar las solicitudes y respuestas, lo que facilita la depuración de las pruebas.
Finalmente, para pruebas que requieren modificar el contexto o inyectar dependencias, podemos utilizar @MockBean y otras herramientas de Spring Boot Test para simular comportamientos:
@WebFluxTest(ControladorReactivo.class)
public class ControladorReactivoTest {
@Autowired
private WebTestClient webTestClient;
@MockBean
private ServicioReactivo servicioReactivo;
@Test
void testServicioMockeado() {
when(servicioReactivo.obtenerElementoPorId(1L))
.thenReturn(Mono.just(new Elemento(1L, "Elemento Mockeado")));
webTestClient.get()
.uri("/api/elementos/1")
.exchange()
.expectStatus().isOk()
.expectBody(Elemento.class)
.value(elemento -> assertEquals("Elemento Mockeado", elemento.getNombre()));
}
}
En este ejemplo, mockeamos el servicio para controlar la respuesta y verificar que el controlador gestiona correctamente el elemento proporcionado por el servicio simulado.
Ejercicios de esta lección Testing de aplicaciones reactivas WebFlux
Evalúa tus conocimientos de esta lección Testing de aplicaciones reactivas WebFlux con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
API Query By Example (QBE)
Identificadores y relaciones JPA
Borrar datos de base de datos
Web y Test Starters
Métodos find en repositorios
Controladores Spring MVC
Inserción de datos
CRUD Customers Spring MVC + Spring Data JPA
Backend API REST con Spring Boot
Controladores Spring REST
Uso de Spring con Thymeleaf
API Specification
Registro de usuarios
Crear entidades JPA
Asociaciones en JPA
Asociaciones de entidades JPA
Integración con Vue
Consultas JPQL
Open API y cómo agregarlo en Spring Boot
Uso de Controladores REST
Repositorios reactivos
Inyección de dependencias
Introducción a Spring Boot
CRUD y JPA Repository
Inyección de dependencias
Vista en Spring MVC con Thymeleaf
Servicios en Spring
Operadores Reactivos
Configuración de Vue
Entidades JPA
Integración con Angular
API Specification
API Query By Example (QBE)
Controladores MVC
Anotaciones y mapeo en JPA
Consultas JPQL con @Query en Spring Data JPA
Repositorios Spring Data
Inyección de dependencias
Data JPA y Mail Starters
Configuración de Angular
Controladores Spring REST
Configuración de Controladores MVC
Consultas JPQL con @Query en Spring Data JPA
Actualizar datos de base de datos
Verificar token JWT en peticiones
Login de usuarios
Integración con React
Configuración de React
Todas las lecciones de SpringBoot
Accede a todas las lecciones de SpringBoot y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Spring Boot
Introducción Y Entorno
Spring Boot Starters
Introducción Y Entorno
Inyección De Dependencias
Introducción Y Entorno
Controladores Spring Mvc
Spring Web
Vista En Spring Mvc Con Thymeleaf
Spring Web
Controladores Spring Rest
Spring Web
Open Api Y Cómo Agregarlo En Spring Boot
Spring Web
Servicios En Spring
Spring Web
Clientes Resttemplate Y Restclient
Spring Web
Rxjava En Spring Web
Spring Web
Crear Entidades Jpa
Persistencia Spring Data
Asociaciones De Entidades Jpa
Persistencia Spring Data
Repositorios Spring Data
Persistencia Spring Data
Métodos Find En Repositorios
Persistencia Spring Data
Inserción De Datos
Persistencia Spring Data
Actualizar Datos De Base De Datos
Persistencia Spring Data
Borrar Datos De Base De Datos
Persistencia Spring Data
Consultas Jpql Con @Query En Spring Data Jpa
Persistencia Spring Data
Api Query By Example (Qbe)
Persistencia Spring Data
Api Specification
Persistencia Spring Data
Repositorios Reactivos
Persistencia Spring Data
Introducción E Instalación De Apache Kafka
Mensajería Asíncrona
Crear Proyecto Con Apache Kafka
Mensajería Asíncrona
Creación De Producers
Mensajería Asíncrona
Creación De Consumers
Mensajería Asíncrona
Kafka Streams En Spring Boot
Mensajería Asíncrona
Introducción A Spring Webflux
Reactividad Webflux
Spring Data R2dbc
Reactividad Webflux
Controlador Rest Reactivo Basado En Anotaciones
Reactividad Webflux
Controlador Rest Reactivo Funcional
Reactividad Webflux
Operadores Reactivos Básicos
Reactividad Webflux
Operadores Reactivos Avanzados
Reactividad Webflux
Cliente Reactivo Webclient
Reactividad Webflux
Introducción A Spring Security
Seguridad Con Spring Security
Seguridad Basada En Formulario En Mvc Con Thymeleaf
Seguridad Con Spring Security
Registro De Usuarios
Seguridad Con Spring Security
Login De Usuarios
Seguridad Con Spring Security
Verificar Token Jwt En Peticiones
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Spring Web
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Reactiva Spring Webflux
Seguridad Con Spring Security
Autenticación Y Autorización Con Anotaciones
Seguridad Con Spring Security
Testing Unitario De Componentes Y Servicios
Testing Con Spring Test
Testing De Repositorios Spring Data Jpa Y Acceso A Datos Con Spring Test
Testing Con Spring Test
Testing Controladores Spring Mvc Con Thymeleaf
Testing Con Spring Test
Testing Controladores Rest Con Json
Testing Con Spring Test
Testing De Aplicaciones Reactivas Webflux
Testing Con Spring Test
Testing De Seguridad Spring Security
Testing Con Spring Test
Testing Con Apache Kafka
Testing Con Spring Test
Integración Con Angular
Integración Frontend
Integración Con React
Integración Frontend
Integración Con Vue
Integración Frontend
En esta lección
Objetivos de aprendizaje de esta lección
- Aprender a ejecutar pruebas de controladores reactivos
- Uso de WebTestClient
- Uso de StepVerifier