archivo

Archivo de la etiqueta: testing

Un esquema tradicional de la implantación del software podría ser el siguiente: 1) instalación en el entorno de integración, 2) Pruebas del sistema en el entorno de integración, 3) Instalación en un entorno de pruebas/preproducción, 4) Validación de el entorno pruebas/preproducción, 5) Instalación en producción.

Que sea tradicional no quiere decir que todos estén de acuerdo con él (y el esquema sea absolutamente distinto) o que sobre el mismo no haya modificaciones.

No entro a valorar si las instalaciones se realizan de forma automatizada o no, no es el objeto del artículo sino de las pruebas o validaciones. Tampoco entro en si se deben realizar pruebas unitarias o automatizadas de mayor nivel, ya que me quiero centrar más en las pruebas de carácter manual que realiza el equipo de testing (si existe) y/o los usuarios.

¿Se debe probar antes de entregar? Por supuesto que los desarrolladores deben hacerlo porque pese a se prueben secciones del sistema durante el desarrollo, es necesario hacer un testing final que afecte al conjunto de funcionalidades entregadas y hacer pruebas de regresión.

¿Debe probar el usuario? Es cierto que se requiere que los usuarios tengan tiempo para realizar esta tarea y en muchos casos, cuando los cambios son significativos y la aplicación compleja, el esfuerzo necesario es importante. Yo soy partidario de que los usuarios prueben en el entorno de integración sobre todo si el impacto funcional del desarrollo es importante (y/o la criticidad del cambio lo aconseja) y si los desarrolladores no tienen un control funcional lo suficientemente sólido.

Otro factor a tener en cuenta es el tiempo medio que se suele tardar desde que se entrega hasta que el producto llega a producción, teniendo en cuenta los posibles rechazos que puede haber en ese proceso.

En cualquier caso, como suelo decir, no se trata de aplicar necesariamente una política general a todos los proyectos sino de realmente analizar las características del sistema (es muy importante tener en cuenta su criticidad), de los cambios realizados, de los usuarios, etc… lo que pasa es que a veces por buscar una armonización de las tareas de testing se suelen establecer una normativa común (que puede admitir o no excepciones motivadas).

También es importante recordar que normalmente el coste de corrección de una incidencia suele ser proporcional al tiempo que ha pasado desde que se ha incorporado al código.

Si se fija al final del sprint una demo o una entrega es necesario dedicar tiempo al testing cuando se hayan finalizado las tareas fijadas en la iteración (previamente los mismos desarrolladores han debido probar el código que han hecho y si hay equipo de testing han debido colaborar en esas tareas conforme se va trabajando) además de otras tareas como puede ser la propia preparación de la entrega (si hay paso a producción o si la instalación en un entorno de demo lo hace un equipo distinto).

Esto quiere decir que la fecha final de los trabajos de desarrollo en la iteración debe ser anterior a la fecha de finalización del sprint. Otra posibilidad es considerar esas tareas de testing como una actividad más dentro de la pila de sprint. El tiempo que se debe dedicar a esta actividad no debe ser exclusivamente el necesario para hacer el testing sino que hay que prever un tiempo para corregir incidencias, ese tiempo lo determinará el equipo en función de la deuda técnica, de la complejidad de las funcionalidades desarrolladas en el sprint y del estado actual del equipo de proyecto (si ha habido cambios en el equipo existirán más posibilidades de que se hayan producido incidencias).

En cualquiera de los dos casos se reduce la cantidad de trabajo que se puede abordar en la iteración pero siempre será mejor eso que entregar software deficiente porque supondrá una resistencia para la próxima (o próximas) iteraciones.

La definición del flujo de trabajo es algo que está abierto y es lógico que así sea porque cada equipo de trabajo y cada proyecto tiene sus peculiaridades.

Es frecuente encontrarnos con tableros que no tienen una fase para el testing y suele ser provocado porque se entiende que de manera implícita se encuentra en la fase de realización, construcción, desarrollo o como se quiera llamar.

Conociéndonos como nos conocemos los desarrolladores y nuestras prácticas habituales de no probar de manera adecuada los componentes que construimos es una buena idea incluir una fase de testing de manera explícita. Es cierto que no te asegura nada (hacer testing requiere de disciplina y buenas prácticas) pero el simple hecho de considerarse una actividad y visualizarse es algo que tiene mucha fuerza.

Por ese motivo, aunque creas que la inclusión de esa fase no aporta nada, prueba a hacerlo y tras un tiempo, trata de sacar conclusiones de manera objetiva a través del número de defectos que se hayan conseguido solventar antes de alcanzar producción en comparación con los valores que estuvieras obteniendo hasta ahora, a partir de ahí ya tendrás todos los datos necesarios para continuar con estas prácticas de manera general, en proyectos o sprints concretos o tomar la decisión de, como hasta ahora, no incluirla.

Aplicando prácticas de Scrum y si me apuráis, por puro sentido común, tenemos que tener como objetivo que el resultado de cada sprint sea una versión entregable y por tanto, que se pueda pasar a producción.

Es muy importante desarrollar con intención para reducir el número de iteraciones (o dicho de otra forma para ganar en cantidad de trabajo efectivo por unidad de tiempo o lo que es lo mismo para ganar en productividad), para ello debemos minimizar (siendo el objetivo máximo eliminar), el número de componentes (funcionalidades) defectuosas que llegan al final del sprint (y por extensión a producción).

Para ello cada historia de usuario debe estar completamente terminada. ¿Qué es eso? Desde luego no es programar las tareas en que se descompone la historia y hacer cuatro pruebas, sino de tener la certeza de que efectivamente funciona, lo que implica una buena batería de testing y la verificación de que efectivamente cumple con las expectativas del usuario.

Una primera aproximación a esto último son las medidas para verificar la misma que aparecen en la historia de usuario, en las cuales tenemos que minimizar las ambigüedades para poder dar por bueno o rechazar la misma, ya que no debería haber medias tintas.

Estas medidas pueden estar implícitas en la propia descripción de la historia o separadas en un apartado diferente de la misma.

Otra cosa es que después, el product owner o por extensión los usuarios, se den cuenta de que la especificación realizada no satisface sus expectativas, en este caso el problema está en la especificación y para solucionarlo se requerirá evolucionar (modificar) esa funcionalidad en uno de los siguientes sprints.

Una tarea (que puede ser una subdivisión de una historia de usuario o una historia de usuario propiamente dicha) puede considerarse terminada bien cuando el desarrollador o desarrolladores la hayan finalizado y probado o bien cuando esa tarea de testing la haga un tercero, en ambos casos, nos puede surgir la siguiente pregunta: si las pruebas han sido correctas, ¿debemos considerar que el tiempo restante es 0?.

¿Qué puede pasar? Que cuando integremos con terceras historias aún en construcción o incluso todavía no iniciadas nos demos cuenta que existen errores en la que hemos dado por terminada y, por tanto, que el tiempo restante ya no sea cero, de manera que nuestras previsiones o expectativas si tomamos como referencia, por ejemplo, un burndown, se vean afectadas, sobre todo, si el defecto encontrado es importante.

La alternativa a no considerar el tiempo restante es 0, es dejar un cierto margen de tiempo restante en función de las probabilidades de error en la integración del componente, de su complejidad, criticidad, etc…, pero en cualquier caso, estaríamos especulando y, pese a que pueda considerarse una postura conservadora, no refleja con fidelidad el estado del sprint.

Por tanto, soy de la opinión de que cuando una tarea se considere terminada (eso sí, con una batería de pruebas ejecutada correctamente y que sea adecuada a su naturaleza) se considere que el tiempo restante sea 0, con independencia de que posteriormente sea necesario realizar reajustes en el mismo como consecuencia de problemas que se vayan detectando.

Me parece muy interesante la siguiente cita de David Gilbert: «La cantidad de formato y nivel de detalle aplicado a un plan de pruebas es inversamente proporcional a la cantidad de buenas pruebas reales representadas por dicho plan».

Esta reflexión es aplicable al resto de documentación generada en un proyecto de desarrollo de software.

La calidad no se mide al peso ni por lo bonito que sea su envase. Es cierto que en ciertos ámbitos esto vende pero en el ámbito de un proyecto su valor no es relevante si no se ha hecho pensando en que sea práctico, útil y acorde a las necesidades.

Un plan de pruebas farragoso no se ejecutará de manera adecuada, en la mayoría de los casos, en muchos de ellos por simple agotamiento o aburrimiento de la persona que debe realizar las tareas de testing y en otros muchos porque entre tanto forraje se perderá lo realmente relevante.

En su día escribí un artículo sobre esta práctica de testing. Hace poco leí una cita de Bob Martin que resume perfectamente los objetivos que se persiguen con el testing exploratorio (traducción libre): «El testing exploratorio es un esfuerzo creativo en el que los testers exploran el comportamiento del sistema en un intento de caracterizar sus comportamientos; tanto documentados como no documentados».

El testing unitario (y en extensión otras prácticas de testing automatizadas) no es suficiente como tampoco lo es el que se basa en planes de pruebas porque ambos dejan o pueden dejar espacios del sistema sin cubrir y que pueden ser importantes.

Es lo que tienen los defectos, cuando menos oportunos son, antes aparecen. De ahí que me parezca muy acertada la siguiente cita de Michael Stahl: «Los bugs irreproducibles se vuelven altamente reproducibles justo después de la entrega al cliente».

Siempre es posible invertir más esfuerzo en la detección de defectos pero es necesario calibrar (todas las partes) qué impacto tiene la ocurrencia de determinados defectos en el software y la disponibilidad económica para hacer ese trabajo.

Es preferible siempre menos pero con más calidad y más garantías, sin embargo, muchos no lo ven así, prefiere llegar a lo que tenían pensado que debería ser el producto sin ponerse a analizar que completo no es lo mismo que correcto.

Cuanto más crítico sea el sistema más exhaustivo se tiene que ser con la localización de defectos y más se debe invertir en su detección lo más próxima posible a su origen (tanto de manera automática como manual), eso es muy importante y se debe tener en cuenta a la hora de hacer estimaciones ya sea como parte de cada historia de usuario o como parte de personas ajenas al equipo que desarrolla el sprint.

Trabajar en planes de pruebas que traten de recoger todas las posibles acciones del usuario requeriría tanto esfuerzo que perfectamente podría consumir todo el presupuesto del proyecto.

El esfuerzo en testing (en general y no centrándome en los planes de prueba que tienen una utilidad parcial y concreta y en los cuales no creo mucho) debe ser siempre proporcional a la criticidad del software y siempre deben existir unos mínimos.

Se deben detectar y corregir los defectos lo antes posible y evitar que lleguen a producción, sin embargo, es tal la casuística que puede provocar un usuario que resulta muy complicado que ninguno traspase esa frontera, de hecho, Elisabeth Hendrickson lo resume perfectamente en la siguiente cita: «Las acciones de los usuarios son siempre variables», no obstante y con independencia de eso, se debe intentar que el número de defectos críticos que lleguen a producción tiendan a cero y para ello es fundamental darle al testing la importancia que se merece.

Me ha parecido muy interesante la siguiente reflexión de Michael Bolton: «Los tests que se han diseñado para confirmar una teoría predominante tienden a no revelar información nueva», y es precisamente por eso, porque se centran en confirmar una hipótesis, sin entrar a estudiar otros aspectos que podrían resultar relevantes.

De hecho, muchos desarrolladores creen que han realizado un testing suficiente y después a las primeras de cambio otra persona, generalmente con más conocimiento funcional, que hace uso de las funcionalidades detecta defectos en poco tiempo, ¿por qué? Su conocimiento del negocio y de las expectativas del usuario es parcial y muchas veces ese testing se centra en aspectos de bajo nivel cuando la solución hace aguas a un nivel más alto.

Esto se extiende al plan de pruebas, entregable muy típico en contextos en los que un equipo independiente realiza un QC (control de la calidad), ¿realmente se confía en que pasando el plan de pruebas el sistema está libre de defectos?, y no solo por el hecho de que muchos de ellos son complicados de detectar, sino porque el plan de pruebas se centra en una visión concreta, en una teoría predominante por parte de la persona o personas que han colaborado en su elaboración. Sí, tal vez confirmemos que la teoría se cumple, pero si dejamos el testing en ese punto, muy probablemente estemos dejando escapar defectos que pueden tener un impacto importante cuando lleguen a producción.

Es por ese motivo que el testing debe ser considerado desde diferentes puntos de vista, en el que se combinen testing automático y manual, en el que intervengan diferentes personas, diferentes perfiles, en el que lo «reglado» se combine con lo exploratorio y en el que se tenga muy claro, qué nivel de defectos puede permitirse el producto y cuánto se está dispuesto a invertir para conseguirlo.