1. Prueba de humo de inicio: qué es
Una prueba de humo de inicio es una prueba que parece que "no hace nada", pero en realidad hace una cosa importante: obliga a Spring Boot a armar el ApplicationContext. Si el contexto se armó, significa que como mínimo no se rompió el cableado, no falló la configuración, no hubo conflicto de beans y la aplicación en general arranca en la configuración de prueba dada. Es como "verificar el encendido" de un coche: todavía no has ido a ninguna parte, pero ya sabes que la batería está viva, el motor de arranque gira y el motor al menos no se cae en el lugar.
Cuando separas el contexto completo del servidor real, la prueba de humo deja de parecer extraña. No necesita un puerto ni un escenario HTTP; necesita una respuesta honesta a la pregunta: ¿la aplicación se arma en la configuración de prueba dada?
La idea clave es que la prueba de humo deliberadamente no toca la lógica de negocio. No intenta verificar PublicationPolicy, no compara JSON, no va a los controladores. Honestamente trabaja a nivel de «el sistema se arma». Y esto no es una "debilidad", sino una fortaleza: la prueba es pequeña, clara en su significado y a menudo detecta roturas que las pruebas unitarias y de slice ni siquiera están obligadas a ver (porque deliberadamente no levantan la mitad de la aplicación).
Para ContentHub, la prueba de humo es especialmente útil como seguro contra los clásicos rastrillos de "integración": alguien cambió YAML, renombró un prefijo de propiedad, no actualizó @ConfigurationProperties, agregó un nuevo bean con el mismo nombre, o activó una auto-configuración que ahora requiere configuración. Todo compila, las pruebas unitarias locales están verdes, pero la aplicación en realidad dejó de arrancar. La prueba de humo lo detectará.
2. Qué demuestra una prueba de humo
Es muy fácil caer en la trampa psicológica: como la prueba se llama "smoke" y está verde, significa que «todo está bien». Luego, producción se cae y miras el cuadrado verde en el informe de pruebas como a un traidor. Pero no es un traidor, simplemente estaba haciendo exactamente lo que prometió: verificar el arranque.
Vamos a fijar esto de la manera más explícita posible. Lo más cómodo es en una tabla, porque al cerebro le encantan las tablas (y además las tablas no discuten con nosotros ni piden café).
| Pregunta | ¿Responde la prueba de humo? | Por qué |
|---|---|---|
| ¿Se levanta el ApplicationContext sin errores de cableado/configuración? | Sí | Este es literalmente el objetivo de la prueba: pasar la fase de inicio del contexto. |
| ¿Se creó el bean clave (por ejemplo, ArticleWorkflowService)? | Sí (si lo verificas) | Se pueden añadir 1 o 2 comprobaciones "ancla", sin convertir la prueba en un escenario. |
| ¿Funciona una regla de negocio concreta (por ejemplo, la transición de estado IN_REVIEW -> PUBLISHED)? | No | Eso ya es responsabilidad de las pruebas unitarias / de flujo de integración, no de la prueba de humo. |
| ¿Es correcto el contrato JSON de los DTO? | No | Esa es el área de @JsonTest y/o pruebas MVC; la prueba de humo va de otra cosa. |
| ¿Funciona correctamente la consulta en el repositorio? | No | Para eso está @DataJpaTest. La prueba de humo no está obligada a demostrar el comportamiento de las consultas. |
| ¿Están configurados correctamente los estados HTTP y el contrato de error? | No | Esta es el área de la capa web (en pruebas MVC estrictas o más adelante en el flujo de cableado). |
| ¿Está demostrado que la aplicación realmente atiende solicitudes HTTP en un puerto? | Depende de webEnvironment, pero la prueba de humo "normal" casi nunca | Si ejecutas con NONE/MOCK, no hay servidor. Y hoy estamos hablando precisamente de la verificación mínima de arranque. |
Si lo decimos en una frase: la prueba de humo es que «la aplicación arranca», no que «la aplicación conduce, aparca, pasa la ITV y además te hace café».
3. Elección de webEnvironment: NONE vs MOCK
En la lección anterior conocimos los modos de webEnvironment. Ahora una pregunta práctica importante: ¿qué modo es el adecuado precisamente para una prueba de humo, si queremos verificar el arranque de la aplicación como sistema, pero no queremos coste adicional ni detalles superfluos?
La mayoría de las veces en un proyecto hay dos opciones razonables para las pruebas de humo de inicio: NONE y MOCK. La diferencia entre ellas no es "cosmética", realmente cambia el tipo de contexto que se levantará. Y aquí es útil pensar de forma pragmática: cuanto menos levantes, más rápida será la prueba, pero menor será la "superficie" que verificas. Cuanto más levantes, más caro, pero mayor la probabilidad de detectar un problema en la configuración web.
Una pequeña ayuda en forma de minitabla:
| Modo | Qué significa en esencia | Cuándo es útil una prueba de humo con él |
|---|---|---|
| NONE | «La aplicación como conjunto de beans, sin entorno web» | Cuando quieres la señal más simple de «el contexto se armó» y no quieres arrastrar la configuración web. |
| MOCK | «La aplicación bajo web, pero sin servidor real» | Cuando quieres que la parte web también se levante, pero sin iniciar un puerto. |
En el contexto de ContentHub (backend MVC), NONE es un muy buen valor predeterminado para la prueba de humo, porque responde a la pregunta mínima y no obliga a la prueba a ser "un poco MVC". Pero si el equipo alguna vez se ha topado con rastrillos del tipo «el contexto se levantó, pero la parte web está rota debido a la configuración/filtros/componentes MVC», entonces una prueba de humo con MOCK puede estar justificada. Es importante recordar que sigue siendo una prueba de humo: no estamos probando el contrato HTTP, simplemente permitimos que la capa web se levante.
4. Prueba de humo mínima: contextLoads()
El camino básico de humo suele tener este aspecto.
Hay una prueba que parece una broma (especialmente para un novato): un método vacío contextLoads(). No tiene assertThat(), no tiene mocks, no tiene comprobaciones. Y sin embargo, puede ser una de las más "valiosas" por lo que verifica: inicia el arranque de la aplicación. Si el arranque falla, la prueba caerá antes de ejecutar el cuerpo del método, en la fase de levantamiento del contexto.
Para ContentHub, normalmente fijamos la prueba de humo como una clase separada con un nombre lo más claro posible. Es muy importante que la prueba utilice un perfil de prueba, de lo contrario puedes obtener fácilmente la situación de «en mi portátil la prueba está verde, pero en CI falla» porque se cargaron configuraciones incorrectas.
Ejemplo de una prueba de humo mínima (presta atención a webEnvironment = SpringBootTest.WebEnvironment.NONE y @ActiveProfiles("test")):
import org.junit.jupiter.api.Test; // anotación básica de prueba
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) // Levantamos el contexto sin servidor web
@ActiveProfiles("test") // Fijamos firmemente el perfil de prueba para no desviarnos a configuraciones aleatorias
class ContentHubApplicationSmokeTest {
@Test
void contextLoads() {
// El cuerpo está vacío: si el contexto no se arma, la prueba caerá antes de ejecutar este método
}
}
Esta prueba verifica una cosa simple: en el entorno test (es decir, con application-test.yml, del que hablaste antes) la aplicación ContentHub es capaz de armar el contexto. No demuestra que un artículo se publique, pero demuestra que puedes llegar al momento en que un artículo teóricamente podría publicarse.
Un beneficio adicional para el desarrollador novato: cuando una prueba así falla, suele fallar de manera muy informativa. En el stacktrace casi siempre se puede encontrar la "raíz del mal": "no se pudo crear el bean", "no se encontró la propiedad", "conflicto de nombres", "error de migración", etc. Sí, al principio el stacktrace parece una "novela de Tolstói", pero contiene capítulos útiles.
5. Prueba de humo con verificación de beans clave
Un contextLoads() vacío está bien, pero a veces se quiere reforzar un poco la prueba de humo para que verifique no solo que "el contexto se armó", sino también que "estas cosas ciertamente existen". Lo principal es no descontrolarse y no convertir la prueba de humo en un escenario de integración de tamaño completo. Una buena prueba de humo sigue siendo pequeña: verifica uno o dos "anclas".
En ContentHub, esas anclas suelen ser ArticleWorkflowService y ArticleRepository. ¿Por qué? Porque atraviesan partes importantes del cableado: la capa de servicio y la capa de datos. Si se inyectan, significa que como mínimo la conexión básica de las capas en el contexto está viva.
Ejemplo de prueba de humo con verificación de dos beans:
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.ActiveProfiles;
import com.example.contenthub.repository.ArticleRepository;
import com.example.contenthub.service.ArticleWorkflowService;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("test")
class KeyBeansSmokeTest {
@Autowired
ArticleWorkflowService workflowService; // Bean ancla: verificamos que la capa de servicio realmente se levantó
@Autowired
ArticleRepository articleRepository; // Bean ancla: verificamos que la capa de datos también está presente en el contexto
@Test
void keyBeansExist() {
// Verificación de humo: constatamos el hecho mismo de la existencia de los beans, no tocamos la lógica de negocio
assertThat(workflowService).isNotNull();
assertThat(articleRepository).isNotNull();
}
}
Aquí es importante mantener la "audacia" correcta de la prueba. No llamamos a workflowService.publish(...), no creamos un artículo, no verificamos el estado. Simplemente constatamos que estos beans se crean. Sigue siendo una prueba de humo: trata sobre el ensamblaje del sistema, no sobre el comportamiento.
Si preguntas: «¿Y eso para qué, si el contexto ya se levantó?», la respuesta es: a veces el contexto puede levantarse, pero un bean concreto puede estar desactivado por una configuración condicional (@ConditionalOnProperty y similares) o reemplazado por algo inesperado. Sí, no es el caso más frecuente, pero verificar dos beans "ancla" a veces simplifica aún más el diagnóstico: ves inmediatamente qué falta.
6. Matices útiles de las pruebas de humo
Anulaciones locales de propiedades
A veces se necesita una prueba de humo no solo para «la aplicación arranca», sino también para «la aplicación arranca con una configuración determinada». Por ejemplo, sospechas que alguien cambió la estructura de la configuración de los archivos adjuntos (contenthub.attachments.*) y ahora el enlace se rompe. O añadiste una nueva configuración y quieres asegurarte de que no entra en conflicto con application-test.yml.
@SpringBootTest tiene un parámetro properties que permite anular valores localmente. Es cómodo, pero tiene un coste: puede generar un nuevo contexto de prueba, y por lo tanto hacer el suite más lento. Recordamos la idea de días anteriores sobre el almacenamiento en caché del contexto: «cada conjunto único de configuración es potencialmente un contexto separado».
Ejemplo de una prueba de humo que verifica el arranque con una anulación local:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = "contenthub.attachments.max-size=1048576" // Anulamos localmente la propiedad para verificar el enlace/validación
)
@ActiveProfiles("test")
class SmokeWithPropertyOverrideTest {
@Test
void contextLoadsWithOverride() {
// Importante: no verificamos el comportamiento, solo el hecho de que el arranque fue exitoso con este conjunto de configuraciones
}
}
Esta prueba, por su significado, verifica: «con esta configuración, el contexto también se arma». Ninguna lógica de negocio. Ninguna solicitud. Ninguna magia de BD. Solo arranque.
¿Cuándo está justificada una prueba así? Cuando realmente existía el riesgo de que «debido a esta configuración la aplicación cayera al arrancar» o «el enlace/validación de la configuración se rompiera». ¿Cuándo no está justificada? Cuando empiezas a hacer diez pruebas de humo con diez valores diferentes, y el suite de repente se vuelve lento porque hay demasiados contextos. La prueba de humo debe ahorrar vida, no quitarla.
Cuántas pruebas de humo mantener en el proyecto
Hay una desafortunada regularidad: en cuanto el equipo conoce las pruebas de humo, aparece la tentación de hacerlas "para todo": una prueba de humo separada para storage, otra para moderación, otra para seguridad, otra para cada perfil, otra para cada... y en algún momento deja de ser "humo" y se convierte en "incendio".
En un proyecto real y vivo, la mayoría de las veces es suficiente con un conjunto muy pequeño: una prueba de humo base ContentHubApplicationSmokeTest y, como máximo, una o dos más que verifiquen ramas especiales de configuración. En el ContentHub de aprendizaje, en general es bueno mantenerse en el minimalismo: una prueba de humo "ancla" y otra "con anulación" (si necesitas fijar una configuración).
¿Por qué muchas pruebas de humo son ruido? Porque casi todas verifican lo mismo: el arranque del contexto. Si el contexto arranca, una segunda prueba de que «el contexto arranca otra vez» no añade nueva confianza, solo añade tiempo de ejecución. Es mucho más útil invertir energía en pruebas de capas más precisas: unitarias, slices, datos, y luego, algunas pruebas de cableado completo bien pensadas.
Hay otro aspecto práctico: las pruebas de humo son especialmente sensibles a cambios "accidentales" en la configuración de prueba. Si en algún lugar activaste una importación extra, añadiste un @TestConfiguration adicional, empezaste a conectar componentes que no son necesarios, la prueba de humo empieza a fallar "de la nada". Por eso, una buena prueba de humo, como una buena tostadora, debe ser simple. Pulsas un botón y funciona. No hay que instalarle un sistema operativo a la tostadora.
7. Lectura de una caída de prueba de humo
Cuando una prueba de humo falla, el novato suele tener dos estados: «he roto el universo» y «Spring me odia». En realidad, Spring es neutral contigo, solo que es muy hablador. Y la prueba de humo es buena precisamente porque la caída suele ocurrir temprano, por lo que la causa suele estar cerca de la superficie.
La técnica práctica para leer una caída es la siguiente. Primero, encuentras en el stacktrace el lugar donde dice algo como Failed to load ApplicationContext. Eso es casi siempre el síntoma superior. Luego buscas Caused by: y bajas hasta que veas una parte más concreta: por ejemplo, que no se creó un bean determinado o que no se enlazaron las propiedades.
Las causas típicas de caída de las pruebas de humo en proyectos Spring Boot (incluyendo ContentHub) suelen ser: configuración incorrecta del datasource (el perfil de prueba no se activó y la aplicación intenta conectarse a la base de datos "real"), error de migración de Flyway (script no válido o conflicto de versiones), conflicto de beans (dos beans del mismo tipo/nombre, y Spring no sabe cuál inyectar), problema con @ConfigurationProperties (cambiaste el prefijo o la estructura del YAML, pero la clase sigue siendo la misma), y, a veces, errores de validación de la configuración (si la clase de propiedades tiene restricciones de Bean Validation).
La prueba de humo no está obligada a enseñarte a vivir sin stacktrace, pero ayuda a que el stacktrace tenga "sentido": sabes con certeza que el problema está en el arranque del contexto. Eso ya reduce mucho la búsqueda. Y sí, a veces la causa es un simple error tipográfico en el YAML. Un error tipográfico muy "caro", pero trivial.
8. Errores típicos al escribir pruebas de humo de inicio
Error n.º 1: considerar la prueba de humo como la "prueba principal" que lo reemplaza todo.
A menudo, después de la aparición del primer contextLoads() verde, surge la ilusión de que «entonces la aplicación funciona». En realidad, la prueba de humo solo demuestra el arranque del contexto. Las reglas de negocio, los contratos JSON, el comportamiento de las consultas de los repositorios y los estados HTTP viven en otras capas de prueba. La prueba de humo es la puerta de entrada, no todo el piso.
Error n.º 2: no fijar el entorno de prueba y ejecutarse accidentalmente sin el perfil test.
Si no se especifica @ActiveProfiles("test") (o una forma equivalente de activar la configuración de prueba), la prueba puede cargar inesperadamente valores incorrectos: por ejemplo, intentar conectarse a una base de datos real, tomar la URL de producción del servicio de moderación o usar una ruta de almacenamiento incorrecta. El resultado son fallos intermitentes de "sin haber cambiado nada". A la prueba de humo le gusta el determinismo: un perfil, YAML claros, sin sorpresas.
Error n.º 3: convertir la prueba de humo en un escenario integral de "media aplicación".
A veces, directamente en la prueba de humo se quiere crear un artículo, enviarlo a revisión, publicarlo y además verificar la salida pública. Eso ya no es una prueba de humo, es una prueba de flujo de integración, y tiene derecho a existir, pero en otro lugar y con otro significado. La prueba de humo debe seguir siendo mínima: el contexto se levantó, como máximo los beans clave están en su lugar.
Error n.º 4: hacer decenas de pruebas de humo con diferentes anulaciones de properties y matar accidentalmente la velocidad del suite.
Las anulaciones locales son útiles, pero cada conjunto único de propiedades puede generar un nuevo contexto, y por tanto aumentar el tiempo de ejecución de todo el lote de pruebas. Si empiezas a "tantear configuraciones" y para cada variante creas un nuevo @SpringBootTest(properties = ...), casi con seguridad obtendrás un conjunto de pruebas lento con un bajo retorno de la inversión. Las pruebas de humo deben ser escasas y estratégicas.
Error n.º 5: verificar en la prueba de humo el comportamiento en lugar del hecho del ensamblaje (y discutir contigo mismo sobre las causas de las caídas).
Si empiezas a llamar a métodos de servicios y verificar resultados en la prueba de humo, entras en una zona extraña: la prueba ya no es una "verificación mínima de arranque", pero tampoco es una prueba de cableado completa con un escenario claro. En ese punto medio, la caída de la prueba se vuelve más difícil de interpretar: o no se levantó el contexto, o el comportamiento no es el esperado, o no se prepararon los datos. La prueba de humo es buena precisamente por la claridad de la pregunta; no conviene difuminarla.
GO TO FULL VERSION