¿Qué pruebas debemos hacerle a nuestro videojuego?

“Los test no son opcionales”. Esto que parece a muchos una verdad, sigue siendo uno de los temas pendientes en el mundo del desarrollo de aplicaciones de software actual.

Sí, increíblemente aún hay muchos compañeros “del metal” que no son conscientes que programar sin pruebas no solo es como hacer acrobacias en el trapecio sin red de seguridad, sino además una fuente de errores, malas prácticas y ansiedad.

Y por ello quiero repasar los fundamentos básicos de las pruebas que debiéramos aplicar, cada uno en su necesidad, a nuestros desarrollos.

/img/c/pruebvideoj.jpg
.

¿Por qué hacer pruebas?

Para que lo entienda hasta el más novel de los lectores, hacer pruebas es la forma de asegurarse que lo que queremos que haga nuestro programa, lo haga, y lo haga sin errores.

La construcción de software implica conocimiento, experiencia, talento, capacidad intelectual y un punto de arte. Es decir, es una labor muy difícil, y falta aún mucho para que eso cambie a mejor. De hecho, la complejidad está tendiendo al crecimiento de una forma absoluta.

Habiendo superado, hace ya décadas, la capacidad humana de aprensión y memorización; lo que implica necesariamente que los fallos y errores son inevitables si los intentamos evitar con solo nuestras capacidades humanas.

Las pruebas no son opcionales. Un software sin pruebas es una bomba a punto de estallar

¿A quien no le ha pasado que ha dejado su código medio año en un cajón, y a la vuelta de ponerse a toquetearlo tener la sensación de que lo ha escrito otra persona? No reconocemos a nuestra propia criatura.Y no hablemos cuando estamos integrados en un equipo, o recibimos el “regalito” de soportar o evolucionar un código heredado.

Por ello las pruebas son imprescindibles, ya que nos permiten garantizar que las aplicaciones cumplen las funcionalidades que se esperan de ellas y las expectativas de calidad (no solo de código); ayudando a encontrar esos errores o defectos que aún no se han descubierto; reduciendo el costo del desarrollo, el de propiedad para los usuarios; y desarrollar confianza en los clientes al evitar los molestos errores de regresión.

Eso sin hablar de la sensación de seguridad incremental que se obtiene cuanto más cerca estamos de un despliegue, ya que a más código que tenemos, más pruebas nos aseguran (en forma de una tupida malla) que todo funciona correctamente.

DevOps y la herencia de la automatización

/img/c/pruebvideoj.jpg
.

La llegada de las metodologías Agiles, desde los años 90 del siglo pasado, fue un revulsivo en la organización y ejecución de pruebas dentro de procesos Waterfall.

En estos últimos, se podría generalizar, las pruebas principalmente eran manuales; definidas meticulosamente en voluminosos documentos de planes de pruebas; y que se realizaban solamente una vez acabada la codificación del software.

Xtreme Programming, en cambio, hizo mucho hincapié en la automatización y en el concepto de pruebas orientadas a la prevención de los finales de los años 80; marcando de esta forma, la futura filosofía Agile. Por ello, actualmente utilizamos frameworks de pruebas que permiten realizar automáticamente la mayoría de los test en todos los ámbitos de la aplicación.

Y más cuando se pretende adoptar el concepto de Integración Continua, como parte imprescindible de DevOps, donde la propia construcción y validación de la Build por medio de todo tipo de pruebas automáticas es parte inherente del proceso.

Siendo esto aún más crítico en niveles altos de madurez en donde llegaríamos a aplicar despliegue automatizado o, incluso, continuo.

La importancia que han ido ganando las pruebas ha sido tal que la propia forma de codificar el software también ha sufrido cambios profundos. El nacimiento de TDD (desarrollo orientado a las pruebas) y su forma de supeditar el código a los test, implica que hacer software testeable es un requisito imprescindible en el código de calidad.

Y, aunque no lleguemos a utilizar esta avanzada técnica de desarrollo (que no es nada fácil), el objetivo de poder probar de forma automática nuestro código, ha reforzado prácticas tan importantes en la programación orientada a objetos como es SOLID.

Un universo de tipos de pruebas

Siguiendo los pasos de la complejidad inherente de nuestra industria, las pruebas también sufren de una miríada inacabable de tipos, versiones, evoluciones y clases. Pero centrémonos en las más importantes e imprescindibles, según cada caso y contexto.

  • Prueba unitaria: las pruebas unitarias son pruebas automatizadas que verifican la funcionalidad en el componente, clase, método o nivel de propiedad.

El objetivo principal de las pruebas unitarias es tomar la pieza más pequeña de software comprobable en la aplicación, aislarla del resto del código y determinar si se comporta exactamente como esperamos. Cada unidad se prueba por separado antes de integrarlas en los componentes para probar las interfaces entre las unidades.

Las pruebas unitarias deben escribirse antes (o muy poco después) de escribir un método; siendo los desarrolladores que crean la clase o el método, quienes diseñan la prueba.

Así, conseguimos mantener el foco en lo que debe hacer el código, y se convierte en una poderosa herramienta para aplicar KISS, JIT, y mantener el foco en lo que tiene que hacer en vez de en el cómo, evitando introducir complejidad sin valor.

  • Pruebas de integración: desde una perspectiva de prueba, las unidades individuales se integran juntas para formar componentes más grandes. En su forma más simple, dos unidades que ya han sido probadas se combinan en un componente integrado y se prueba la interfaz entre ellas.

Las pruebas de integración – o de componentes - identifican problemas que ocurren cuando las unidades se combinan. Los nuevos errores que surgen probablemente estén relacionados con la interfaz entre las unidades en lugar de dentro de las propias unidades; simplificando la tarea de encontrar y corregir los defectos.

  • Pruebas de regresión: cada vez que se realizan cambios en un proyecto, es posible que el código existente ya no funcione correctamente o que se presenten errores no descubiertos previamente. Este tipo de error se llama regresión.

Para detectar estos defectos, todo el proyecto debe someterse a una regresión: una nueva prueba completa de un programa modificado, en lugar de una prueba de solo las unidades modificadas, para garantizar que no se hayan introducido errores con las modificaciones.

Como se puede deducir, este tipo de pruebas debe ser automatizado porque puede estar compuesto por decenas o miles de pruebas unitarias, de integración o más.

Una versión menos costosa, podría ser construir pruebas que repliquen las acciones que provocaron la regresión, y comprueben que han sido corregidos al no volver a sucederse los errores; además de añadir los test unitarios que aseguren que el código que ha corregido la regresión funciona correctamente.

  • Pruebas de funcionalidad: pruebas automatizadas o manuales que prueban las funcionalidades de la aplicación o módulo construidos desde el punto de vista del usuario final, con sus diferentes roles, para validar que el software hace lo que debe y, sobre todo, lo que se ha especificado.

En su versión automática son pruebas que se automatizan para “ahorrar tiempo de pruebas”. A partir de los casos de prueba de las pruebas manuales, se automatizan los casos de prueba para que se repitan en las ejecuciones. Esos casos suelen ser los más importantes (happy flow) de los módulos o procesos de negocio “vitales” de la aplicación. Es decir, los procesos que siempre tienen que funcionar y que bajo ningún concepto pueden fallar. El objetivo de las pruebas funcionales automáticas es comprobar que no haya regresiones.

  • Pruebas de estrés: las pruebas a pequeña escala, como un usuario único que ejecuta una aplicación web o una base de datos con solo un puñado de registros, pueden no revelar problemas que suceden cuando la aplicación se usa en condiciones “reales”.

La prueba de estrés empuja los límites funcionales de un sistema. Se realiza sometiendo el sistema a condiciones extremas, como volúmenes de datos máximos o una gran cantidad de usuarios simultáneos.

También se utilizan para, llevado el sistema al colapso o degradación, comprobar su funcionamiento continuado por encima de su límite y, una vez liberado de la carga, evaluar su capacidad de resiliencia volviendo a su estado óptimo de funcionamiento.

  • Prueba de rendimiento: determinan la capacidad de respuesta, el rendimiento, la confiabilidad y/o la escalabilidad de un sistema bajo una carga de trabajo determinada.

En aplicaciones web, las pruebas de rendimiento a menudo están estrechamente relacionadas con las pruebas de estrés, la medición del retraso y la capacidad de respuesta bajo una carga pesada.

En otras aplicaciones (escritorio y aplicaciones móviles, por ejemplo), las pruebas de rendimiento miden la velocidad y la utilización de recursos, como el espacio en disco y la memoria.

  • Pruebas de seguridad: validan los servicios de seguridad de una aplicación e identifican posibles fallos y debilidades.

Muchos proyectos utilizan un enfoque de caja negra para las pruebas de seguridad, lo que permite a los expertos, sin conocimiento del software, probar la aplicación en busca de agujeros, fallos, exploit y debilidades.


/img/ref.png
.