archivo

Archivo de la etiqueta: pruebas de regresión

En sistemas donde la deuda técnica es alta la definición y ejecución de pruebas de regresión resulta esencial. Cuando la situación del código y la arquitectura es tan mala no es suficiente con tener la sensación de que se tiene controlado todo lo que se modifica sino que además es necesario verificar que el sistema sigue funcionando de manera adecuada, incluso en aquellas partes del mismo donde se piensa que es imposible que tengan efecto los cambios realizados.

Sin pruebas de regresión estamos prácticamente jugando a los dados en cada paso a producción y un sistema en producción es demasiado serio como para esperar que la suerte siempre esté de nuestro lado.

Nada es infalible, las pruebas de regresión tampoco (lo más probable es que en sistemas con un tamaño moderado no exista la posibilidad de invertir el esfuerzo necesario para definirlas y/o ejecutarlas pero siempre se debe llegar al mínimo de probar todas las funcionalidades esenciales y críticas del sistema) pero cuanto más precavidos seamos menos sorpresas desagradables nos encontraremos después.

Existe un cierto grado de confusión entre lo que es smoke testing y sanity testing, sin embargo, las diferencias entre ambas técnicas son significativas, ya que sanity testing consiste en realizar pruebas de regresión tras un cambio menor que se realizan sobre una funcionalidad o un conjunto de funcionalidades concretas del sistema (constituye por tanto, un subconjunto del total de pruebas de regresión que se pueden definir o realizar sobre la aplicación).

Este testing se debe realizar con detalle pero sin salirse de lo que es el nivel de validación funcional.

¿Cuánto tiempo dedica tu equipo de proyecto a realizar testing?, ¿cuál es la proporción respecto al esfuerzo dedicado netamente a desarrollar?, ¿se utilizan estrategias que automaticen determinados tipos de test como por ejemplo las pruebas unitarias?, ¿vas más allá y aplicas TDD?, ¿usas integración continua?, ¿en qué etapas realizas pruebas?, ¿comienzan desde el mismo proceso de análisis?, ¿cuál es la participación del usuario (o el cliente en general) en las mismas?, ¿en qué proporción es explotaratoria y en qué proporción se basa estrictamente en casos de prueba?, ¿participan personas distintas al equipo de proyecto en pruebas de tipo funcional?, ¿se realiza testing de seguridad, usabilidad, rendimiento?, dicho testing, ¿cuánto es manual y cuánto está asistido por herramientas?, ¿no se realizan pruebas de la aplicación en un entorno de integración del cliente?, ¿realizas análisis estático de código?, en dicho análisis, ¿tienes definidos unos umbrales mínimos de calidad exigibles a las distintas métricas?, cuando vas a entregar una nueva versión de un producto software, ¿haces pruebas de regresión?.

Aunque tal vez todo lo anterior se pueda resumir en par de preguntas, ¿qué nivel de importancia le das al testing en las aplicaciones que desarrollas?, ¿tienes mecanismos para comprobar si se realiza testing y para evaluar la efectividad del mismo?.

El testing se infravalora de manera muy injusta. Bien realizado ahorra muchos problemas y por encima de eso, permite conservar tu imagen y tu dinero.

Hay muchos que piensan que el testing no es ágil y yo respeto las opiniones de cada uno, independientemente de que las comparta o no. Como he dicho muchas veces, lo que no es ágil es tener que repetir trabajo no por evolucionar una funcionalidad o un componente software, sino para corregir errores que perfectamente detectables en etapas anteriores.

Es más ágil (y menos arriesgado) prevenir un incendio que intentar sofocarlo después.

Boris Beizer es un autor e ingeniero de software americano (aunque nacido en Bélgica) especializado en el campo de la calidad del software y del testing. Sus primeras publicaciones datan de finales de la década de los cincuenta, aunque su etapa más prolífica fue en las décadas de los setenta y de los ochenta.

El testing es un campo dentro de la ingeniería del software un tanto controvertido. La mayoría de los profesionales consideramos necesarias las pruebas en el proyecto de desarrollo de software, si bien no hay coincidencia en cuanto a la intensidad, los momentos y la metodología.

Soy de la opinión de que no todos los proyectos y sistemas de información deben tener un mismo tratamiento y que independientemente de que exista una cierta metodología en las pruebas, estas deben girar alrededor del testing ágil y del exploratorio.

También soy partidario de que las clases con mayor acoplamiento sean cubiertas mediante pruebas unitarias (no necesariamente desarrollando según técnicas de desarrollo guiadas por las pruebas (TDD)) y que se deben minimizar los efectos colaterales a través de las pruebas de regresión.

En cuanto a los momentos, el testing debe aplicarse desde las etapas más tempranas del software, si bien los actores pueden ser distintos a las pruebas realizadas sobre el producto.

Y como estrategia, la utilización de integración continua permite detectar problemas de carácter unitario y de integración lo antes posible y a reducir los problemas en la fase de implantación.

Boris Beizer realiza la siguiente reflexión (traducción libre): «Una amenaza bien vale mil pruebas».

Desgraciadamente nos acordamos de un proceso de testing bien realizado cuando ya los errores han salido a la luz y lo peor de eso, cuando su corrección requiere de un esfuerzo considerable, cuando ha impactado gravemente en el negocio o cuando nos crea un problema.

Cuando mayor sea la criticidad del sistema más peso debe tener el proceso de testing y más peso debe cobrar la metodología y sistematización.

Si problemático resulta el desarrollo de software, el paso a producción en muchos casos no lo es menos. La implantación de un sistema de información con una cierta envergadura no es un proceso trivial y es necesario, en el caso de que el tiempo que se tarde no sea bueno, medir y mejorar el proceso para intentar alcanzar una mejora continua que lleve a unos umbrales aceptables.

Jim Highsmith, firmante del manifiesto ágil, hace una reflexión sobre este tema en su artículo «Shortening the tail«.

Este autor denomina como «tail» a todo el proceso posterior a que una evolución de un producto se considere terminada por parte del equipo de proyecto. En este proceso se incluirían las actividades de testing, pruebas de regresión, integración, pruebas de integración, documentación, corrección de incidencias de la entrega y paso a producción.

De hecho la definición exacta que da de tail es todavía más elocuente (voy a dejar algunas palabras en inglés para no restar significado): Es el tiempo transcurrido desde el «code slush» o «feature freeze» hasta el despliegue del producto.

Comenta Jim Highsmith que el tail más largo que encontró fue de dieciocho meses. Afortunadamente no he tenido uno tan largo, los peores tiempo con que me he encontrado han sido de tres o cuatro meses, si bien, ha sido debido generalmente a que el proceso de desarrollo lo había fraccionado en varias entregas, lo que daba lugar a que el tiempo se repartiera entre las mismas. Probablemente si sumara los tiempos de las mismas serían muy superiores a los meses que he indicado.

Tail de larga duración para entregas o evoluciones simples son un problema, ya que marcan el tiempo en el que un usuario por término medio podrá disfrutar de una evolución del producto (no cuento los posibles parches rápidos que se puedan instalar, ya que estos, generalmente tienen otro proceso de implantación).

Estas evoluciones en muchos casos no son caprichos, sino necesidades que si no son cubiertas provocan una ineficacia en el trabajo desarrollado por los usuarios y en consecuencia un impacto en los resultados del departamento y en consecuencia de la organización. Es por ello que se debe dar la importancia que se merece la optimización de estos tiempos.

Se entrega una iteración de un sistema, se realiza una evolución del mismo mediante tareas de mantenimiento evolutivo o se corrigen incidencias y no paran de producirse efectos colaterales, una vez, otra vez y otra vez.

Además de dar muestras de que no se realizan pruebas de regresión de manera adecuada (o directamente no se llevan a cabo), el sistema de información da muestras de tener un diseño y/o una codificación deficiente, ya que cuando se toca un componente empiezan a salir grietas por todos lados.

Cuando esto pasa, la sensación de desconfianza entre los usuarios o los responsables tećnicos del cliente es tal que cualquier cambio en el sistema da sensación de pánico y lo peor de todo es que esas sensaciones se terminan convirtiendo en un hecho.

Cuando nos encontramos con estos problemas, hay que estudiar cuanto antes su origen. A veces serán aspectos concretos que se podrán más o menos controlar y otras veces serán tantas las minas en el código que cualquier cambio presenta un riesgo de efectos colaterales.

Una vez detectadas las causas toca tomar decisiones que dependerán de la envergadura del problema, de la disponibilidad presupuestaria y de la frecuencia con que se esperen tareas de mantenimiento sea del tipo que sea.

Una continuación de este artículo se puede leer en Testing Baires.

Sucede con demasiada frecuencia que cuando se entrega una evolución de un software, ya sea en una iteración en un desarrollo iterativo incremental o en un mantenimiento correctivo o evolutivo, se realiza testing exclusivamente sobre las funcionalidades que se han desarrollado y no se comprueban otros segmentos del programa que se han podido ver afectados por los cambios realizados en el código.

En muchos casos son las prisas por cumplir plazos en el proyecto, en otros por la necesidad de solucionar una urgencia y en otros tantos porque no se piensa que determinados cambios en el código puedan provocar efectos colaterales en la aplicación.

Las posibilidades de que se produzcan este tipo de problemas dependerá mucho de la calidad de la codificación y de la sección del código que se esté tocando, ya que no es lo mismo tocar una clase con un alto acoplamiento que otra.

Para reducir el riesgo de que aparezcan efectos colaterales, es conveniente la realización de pruebas de regresión que se basan principalmente en realizar testing sobre funcionalidades de la aplicación que presentan riesgo de haber sido afectadas por los cambios. El esfuerzo necesario en realizar estas pruebas dependerá del nivel de automatización de las mismas que se tenga hasta el momento en el rango que va desde las pruebas unitarias hasta las funcionales.

Sobre las pruebas de regresión y en general sobre el hecho de que en la programación no hay que dar nada por sentado, podemos recordar la siguiente cita de Doug Linder: «Un buen programador es aquel que siempre mira a ambos lados antes de cruzar una calle que tiene un único sentido”.

Muchas veces nuestro afán por querer solucionar los problemas de un sistema de información o simplemente mejorarlo, produce efectos contrarios a los que se desean y este hecho me ha pasado en bastantes ocasiones y es otro de los aspectos en los que debo ir mejorando poco a poco.

¿Qué es lo que pasa? Pues que si subimos parches muy frecuentemente y estos no se hacen en momentos del día o de la semana controlados, pueden provocar:

1) Que durante la ventana de tiempo en que se están subiendo exista una pérdida de disponibilidad de parte de la aplicación o incluso de la aplicación completa.

2) Que arreglen un problema, pero provoquen otros. Si el volumen de parches que se sube es bastante alto, se incrementan las posibilidades de que algunos de ellos no se haya terminado de probar lo suficientemente bien (sobre todo en pruebas de regresión) y empiece a dar problemas, que puedan provocar un funcionamiento anómalo de determinadas funcionalidades, la pérdida de disponibilidad de las mismas o la pérdida de disponibilidad del sistema. Por este motivo un parche que da problemas puede dar lugar a otros parches para corregirlo y hacer que se entre en un bucle que no es positivo para nadie, ni para los usuarios, ni para el cliente, ni para el proveedor.

Es cierto que una vez que se solucionan estos problemas, el sistema estará mejor que antes, pero esta mejora no necesariamente justifica haber pasado por situaciones como las que he comentado, ya que además de provocar problemas a los usuarios que se traducen en incidencias que hay que solucionar (y que pueden suponer un gran esfuerzo), se produce una pérdida de credibilidad en el sistema de información y crea en el usuario la sensación de que casi siempre hay algo que falla o que les impide hacer su trabajo de manera adecuada. Estas sensaciones terminarán por eclipsar cualquier mejora que se haya añadido al sistema.