Diseño para la capacidad de prueba: Rompiendo la próxima barrera
4 min de lectura

En nuestra serie actual DevOps para el Éxito, hemos estado documentando nuestro viaje de transformación DevOps. En el Capítulo 1, presentamos el Triángulo DevOps: Herramientas, Arquitectura y Personas en el Desarrollo de Productos, donde discutimos los elementos fundamentales que impulsan el éxito de DevOps. En el Capítulo 2, exploramos Los Primeros Pasos en Nuestra Transformación DevOps, compartiendo cómo sentamos las bases para cambios significativos.

Ahora, en el Capítulo 3, abordamos el siguiente gran desafío que encontramos: Diseño para la capacidad de prueba. Después de mejorar nuestra estructura de equipo, perfeccionar nuestra Definición de Hecho e implementar el análisis de código automatizado, nos dimos cuenta de que el principal cuello de botella en nuestro proceso se había trasladado al diseño de software, específicamente, a la capacidad de prueba. El trabajo se acumulaba en la fase de pruebas, impidiéndonos lograr ciclos de lanzamiento más rápidos. Este capítulo profundiza en cómo superamos este desafío y mejoramos nuestra capacidad para entregar software de alta calidad de manera eficiente.

El cuello de botella de las pruebas

La razón principal de este retraso fue que las pruebas eran predominantemente manuales, centrándose en las pruebas de interfaz de usuario (UI) y los flujos de trabajo del usuario. Dado que la UI solía ser uno de los últimos elementos en completarse, había poca o ninguna automatización implementada, y las pruebas manuales se convirtieron en el único enfoque viable. Los casos de prueba de UI automatizados (por ejemplo, pruebas de Selenium) se escribían típicamente después de completar las pruebas manuales, principalmente con fines de regresión.

Esta dependencia de las pruebas manuales resultó en:

  • Retrasos significativos en los lanzamientos de software.
  • La necesidad de múltiples despliegues para permitir pruebas paralelas por parte de más de un miembro del equipo de QA.

Esto resaltó la importancia de hacer el código más fácil de probar para optimizar el flujo de valor. Sin embargo, lograr esto resultó ser más difícil de lo que habíamos anticipado.

Priorización de las pruebas de API y las pruebas unitarias.

Para abordar este desafío, nos centramos en dos áreas principales:

  1. Pruebas Unitarias - Asegurando que los componentes individuales puedan probarse de forma aislada.
  2. Pruebas de API - Reducir la dependencia de las pruebas basadas en la interfaz de usuario y de las dependencias de la base de datos.

Pruebas de API: Desplazamiento a la izquierda para una retroalimentación más rápida

Redefinimos nuestra estrategia de pruebas de API con estas mejoras clave:

  1. APIs bien definidas: Las APIs se diseñaron para ser sencillas, estar bien documentadas y disponibles en las primeras fases de desarrollo.
  2. Evitar la base de datos como punto de integración - Depender de las bases de datos para la integración creaba dependencias que ralentizaban las pruebas. En su lugar, avanzamos hacia la integración basada en API utilizando REST sobre HTTP y GraphQL. Esto minimizó el tiempo de configuración de la base de datos y mejoró la automatización de las pruebas.

Este cambio redujo significativamente los retrasos, permitiendo una automatización más rápida y pruebas en etapas tempranas, demostrando que centrarse en diseños API-first mejoró tanto la capacidad de prueba como la eficiencia.

Pruebas Unitarias: Un Cambio de Mentalidad

Inicialmente, nuestros esfuerzos de pruebas unitarias llevaron a un rápido aumento de la cobertura de pruebas, pero rápidamente identificamos problemas:

  • Algunas pruebas eran superficiales, escritas solo para cumplir los objetivos de cobertura de código.
  • Carecían de una validación significativa de la funcionalidad, los casos límite y los escenarios de fallo.

Para contrarrestar esto, destacamos:

  • Capacitar a los desarrolladores sobre cómo escribir pruebas valiosas.
  • Refactorización de código para mejorar la capacidad de prueba.

Desafíos al escribir código comprobable

El mayor obstáculo para las pruebas unitarias eficaces fue la falta de capacidad de prueba en el propio código base. Los problemas clave incluyeron:

  • Grandes funciones monolíticas.
  • Componentes fuertemente acoplados.
  • Mala separación de responsabilidades.
  • Abstracción insuficiente.

Estos problemas dificultaron el aislamiento y la prueba efectiva de unidades individuales. Para abordar esto, nosotros:

  1. Se proporcionaron directrices.: Compartimos las mejores prácticas sobre:
    • Selección de funciones para pruebas unitarias.
    • Refactorización de código para mejorar la capacidad de prueba.
    • Diseño y uso de simulacros para facilitar las pruebas.
  2. Centrado en mejoras incrementales: Se animó a los desarrolladores a realizar cambios pequeños y significativos para mejorar la capacidad de prueba con el tiempo.

Progreso Lento pero Constante

A pesar de estos esfuerzos, el progreso fue lento, especialmente para los productos heredados. La arquitectura existente limitaba el número de pruebas unitarias que podíamos escribir. Sin embargo, estas acciones no solo estaban destinadas a mejorar la base de código actual, sino que eran una inversión en el futuro:

  • Mejorar las habilidades de los desarrolladores en el diseño de código comprobable aseguró que los proyectos futuros no sufrirían los mismos problemas.
  • Mejoras incrementales evitaron interrupciones mientras aumentaban constantemente la cobertura de la automatización de pruebas.
  • Un cambio de mentalidad ayudó a los equipos a ver la capacidad de prueba no como una carga, sino como una necesidad para el éxito sostenible de DevOps.

Para los equipos y organizaciones que se embarcan en una transformación DevOps, la capacidad de prueba es un área crítica de atención. Ayuda a aliviar los cuellos de botella en la fase de desarrollo. Sin embargo, es importante gestionar las expectativas: los resultados inmediatos pueden no ser posibles, especialmente al tratar con grandes bases de código heredadas. Refactorizar dicho código sin suficientes pruebas unitarias y de regresión es un desafío importante.

Lecciones de El Programador Pragmático

Para escenarios en los que empezar con una nueva base de código no es factible, The Pragmatic Programmer: Your Journey to Mastery, de Andy Hunt y David Thomas ofrece orientación práctica:

  • Cambios Incrementales Dirigidos: Concéntrese en refactorizar partes pequeñas y manejables del código.
  • Desacoplar componentes: Reducir las dependencias para facilitar la prueba de las unidades individuales.
  • Dividir funciones monolíticas: Divida las funciones grandes en unidades más pequeñas y enfocadas.
  • Adoptar un diseño modular: Hacer el código más fácil de probar y mantener mejorando la modularidad.

El mensaje aquí es claro: una vez que se hayan abordado los cuellos de botella iniciales, la capacidad de prueba debería convertirse en un área de enfoque clave. Esto facilitará las limitaciones en la fase de prueba y permitirá una entrega más rápida de software de alta calidad.

Perspectivas

Nuestro camino para mejorar la capacidad de prueba nos enseñó valiosas lecciones sobre el papel del diseño de software para permitir transformaciones fluidas de DevOps. Al centrarnos en la capacidad de prueba, abordamos los cuellos de botella, mejoramos las habilidades de los desarrolladores y sentamos las bases para el éxito futuro. Si bien los resultados inmediatos pueden ser lentos, los beneficios a largo plazo en términos de calidad, eficiencia y crecimiento del equipo hacen que esta inversión valga la pena.

A medida que continuamos nuestro viaje DevOps, medir el éxito se convierte en el siguiente desafío clave. En el Capítulo 4, exploraremos cómo establecimos KPIs y Paneles de Control para seguir nuestro progreso e identificar nuevas áreas de mejora.

Manténgase atento mientras profundizamos en cómo la toma de decisiones basada en datos nos ayudó a perfeccionar nuestros procesos de DevOps y optimizar el rendimiento!

Suscribirse al Blog de Freyr

Política de privacidad