En muchas ocasiones nos encontramos proyectos donde las pocas pruebas que se realizan son siempre en la etapa final, ya sean a mano o automatizadas.
Si la calidad no está siendo contemplada en todas las etapas del desarrollo y queremos empezar a aplicar sentido a las pruebas, la Pirámide de Testing nos abruma con la cantidad de trabajo que tenemos pendiente de hacer.
Entonces, ¿por dónde empezar?
Pues por donde más necesidad tengamos…
Sé que a priori es una respuesta muy ambigua pero lo que vamos a explicar en este post es un resumen de los diferentes tipos de pruebas que conforman la Pirámide de Testing y qué problemas solventan cada una de ellas. De esta manera podrás decidir tú mismo cuál de ellas merece el foco del esfuerzo.
¿Qué es la Pirámide de Testing?
Si eres de las pocas personas que no ha oído hablar de este término, a continuación vamos a explicar brevemente en qué consiste:
La Pirámide de Testing (“Succeeding with Agile” – Mike Cohn) representa el volumen de pruebas según su tipo. Cuanto más ascendemos por los peldaños de la pirámide más lentas y costosas serán las pruebas que realicemos. Debido al coste, el grado de automatización también debería disminuir a medida que ascendemos peldaños de la pirámide.
Hay que tener en cuenta que la Pirámide de Testing puede variar en función del tipo de arquitectura de software que tiene el proyecto. No me voy a poner a hablar de arquitecturas ya que daría para otro post, pero hoy en día las arquitecturas están variando mucho y por ejemplo los ingenieros de Spotify nos hablan de la forma de panal de abeja para cuando tenemos una arquitectura basada en microservicios.
Hay que tener en cuenta que la Pirámide de Testing puede variar en función del tipo de arquitectura de software que tiene el proyecto.
Tanto la Pirámide de Testing como la forma de panal de abeja indican la cantidad de esfuerzo que tenemos que poner a las pruebas, por lo que es muy importante conocer nuestro sistema para saber probarlo de una manera eficiente y dedicar el esfuerzo que merece cada una de las capas.
Si queréis profundizar más en el tema, existen otros diseños como la forma de trofeo para enfocar las pruebas frontales. Por otro lado os dejo por aquí una pequeña reflexión de Martin Fowler hablando de los diferentes diseños propuestos.
La Pirámide de Testing la componen 5 niveles: Unitarios, Contratos, Integración, End-to-End y de Sistema.
Tests Unitarios
En la base de la pirámide encontramos los test unitarios, estos test se encargan de probar que el código que escribimos se comporta como esperamos. Los test unitarios prueba de manera aislada una parte del código en su totalidad.
Estos test nos van a ayudar a anticipar los errores que se puedan producir y tener controlados los elementos al más bajo nivel. Tener una buena cobertura de este tipo de test hace que sepamos qué y dónde falla nuestro código.
Generalmente estos test se tienen que entregar junto con la tarea que desarrollamos, siempre se deberían de estimar y tener en cuenta a la hora de desarrollar la tarea. Es obligatorio que las tareas lleven siempre sus test unitarios.
¿Son tan importantes como para priorizar el esfuerzo por encima de otros?
Personalmente me gusta plantear una pregunta: cuando subimos un hotfix que arregla un bug, ¿acabas generando 3 bugs en otra parte de la aplicación?
Si es tu caso, los test unitarios son los test más rápidos de ejecutar, menos costosos de desarrollar, más fáciles de mantener y muy robustos. Tenemos que procurar que estos test cubran la mayor parte de la aplicación y de ésta manera vas a detectar rápidamente qué está fallando en tiempo de compilación.
¿Cómo implementarlos?
Cada lenguaje de programación tiene muchas librerías de desarrollo de test unitarios, aquí vamos a citar algunas de las más usadas. Por ejemplo, si estamos desarrollando con Java se puede utilizar JUnit y Mockito. Si estamos desarrollando con Javascript podemos utilizar Mocha, Chai y Sinon. Por último, si estamos desarrollando con Python se puede utilizar Pytest.
Tests de Contratos
Los test de contratos son un tipo de tests que permiten verificar la comunicación entre dos o más servicios. Estos test se dividen en dos partes: el consumidor del servicio y el productor del servicio. Tanto la ejecución como la implementación pueden correr a cargo de diferentes equipos.
Entre el consumidor y el productor del servicio se realiza un contrato y tiene que ser respetado por ambas partes.
A partir de este contrato el consumidor puede realizar los test de manera unitaria integrándose con un mock del proveedor. A su vez, el proveedor hará lo mismo y podrá realizar los test mockeando las llamadas del consumidor.
De esta manera cuando ambas partes se integren no se producirá ningún tipo de error.
¿Son tan importantes como para priorizar el esfuerzo por encima de otros?
Si tienes muchas dependencias con otros equipos y a la hora de integrar todas las piezas se cometen errores de comunicación esta es tu solución.
Los test de contratos ayudan a que todas las piezas cumplan el contrato establecido de comunicación entre componentes. Si yo te mando A y tú me devuelves C no puedes romper este contrato sin tenerme en cuenta y devolver cualquier otra cosa distinta a C. De esta manera detectamos y reducimos de una manera temprana los problemas de integración.
¿Cómo implementarlos?
La herramienta más utilizada para realizar test de contratos es Pact. Pact es una herramienta multilenguaje, que incluye soporte para Java, JS, Python, C++, etc. De esta manera permite que diferentes dependencias escritas cada una en su lenguaje de programación puedan respetar los contratos acordados.
Tests de Integración
Los test de integración son aquellos que se realizan para comprobar las interacciones del componente a probar con el resto de dependencias. Estos test nos ayudan a validar que el componente se comporta como debería.
¿Son tan importantes como para priorizar el esfuerzo por encima de otros?
Permíteme contestar esta pregunta planteando otra: ¿alguna vez en el proyecto el QA ha tenido que acceder a BBDD para dar por bueno un test e2e?
Si es tu caso deberías priorizar los test de integración y comprobar que cada uno de tus componentes se integra o comunica de manera correcta con elementos dependientes del sistema. Esto te permitirá agilizar y eficientar las pruebas que desde las capas más altas son imposibles de replicar o muy costosas de realizar.
¿Cómo implementarlos?
Los test de integración dependen totalmente de cómo sea tu software. Existen mil maneras de probar de manera integrada los componentes. Por ejemplo, si tenemos un servicio web, se podría llamar a dicho servicio a través de una aplicación como Postman o SoapUI. Si nuestro software está escrito con Java se puede utilizar JUnit teniendo en cuenta que cuando invocamos la funcionalidad no hay que mockear nuestra dependencia. Si tenemos un proyecto con Spring Boot, podemos utilizar la propia librería de Spring Boot llamada Spring Boot Starter Test.
Tests End To End (e2e)
Los test end-to-end son aquellos que simulan la experiencia de usuario paso a paso y verifican que nuestro software funciona de principio a fin.
Estos test generalmente parecen que son los que más valor aportan, ya que garantizan que la aplicación funciona correctamente de principio a fin. Pero esto es una verdad a medias, ya que normalmente se suele pecar de querer probar todo desde este nivel y cometemos el error de llenar todo de pruebas e2e y dejar de hacer las anteriores.
En realidad se deberían hacer los test justos para validar que damos el servicio principal. El resto de funcionalidades se prueban desde las capas más bajas donde podemos identificar el punto exacto del error y podemos llegar a todas las casuísticas. Si lo hacemos desde esta capa es imposible e incluso resulta tan caro que no merece la pena. Como bien indica la Pirámide de Testing estos son muy costosos de realizar, muy frágiles, tardan mucho en ejecutarse y requieren mucho mantenimiento.
¿Son tan importantes como para priorizar el esfuerzo por encima de otros?
Pues todo depende de la cantidad de pruebas que realices, si no haces nada de lo anterior por lo menos asegúrate de que lo que estás entregando funciona y prioriza este tipo de test, ya sea de manera manual o automatizada.
¿Cómo implementarlos?
Estos test se pueden realizar de manera manual y se pueden automatizar. Dependiendo de si queremos realizar las pruebas e2e desde un navegador web, una aplicación móvil o una API, se pueden utilizar diferentes tipos de herramientas para automatizarlas. Existe una gran variedad de ellas y voy a poner algunos ejemplos para cada una de estas opciones. Para navegador web se pueden automatizar utilizando aplicaciones como Cypress, Selenium o Pupeteer. Para una aplicación móvil se puede utilizar Appium o Espresso y para APIs podemos utilizar Postman o REST-Assured o Chai HTTP.
Tests de Sistema
Los test de sistema son aquellos que se encargan de probar diferentes aspectos de nuestro sistema una vez finalizado. Tales como el rendimiento, la usabilidad, resiliencia etc.
Estos test ayudan a que nuestro software y sistema reaccione y se adapte correctamente al entorno productivo.
Entrar a definir cada uno de los diferentes test de sistema puede dar para otro post, pero vamos a poner de ejemplo los test de rendimiento. Los test de rendimiento permiten conocer si nuestro sistema proporciona un tiempo de respuesta esperado, proporciona servicio a un número definido de usuarios o procesa cierta cantidad de datos sin producir ningún tipo de error o caída.
¿Son tan importantes como para priorizar el esfuerzo por encima de otros?
¿Tu aplicación sufre reinicios o pérdidas de servicio esporádicas? Si dispones de un sistema ya productivo y quieres que se comporte como esperas frente a la demanda del mercado seguramente te interese verificar que tu sistema va a cumplir todas tus necesidades.
¿Cómo implementarlos?
Conclusión
Todos los peldaños de la pirámide se tienen que realizar y con el volumen adecuado. Ya sea más tarde o más temprano deberías completar todos los peldaños de la pirámide si quieres tener tu software bien probado y que sea mantenible en el futuro.
Espero que este post os ayude a entender para qué sirve cada prueba y aplicarlas en consecuencia, para no buscar desde uno de los peldaños todo el volumen de las pruebas y así ahorrarte sustos a futuro.


