Android — ¿Por qué no estás escribiendo tests? (1)
Be clean, be testeable
Es un tema recurrente año tras año: un buen número de desarrolladores no escriben tests (pruebas) para sus aplicaciones y las razones puede ser diversas: Desde “no sé cómo escribir tests” hasta “escribir tests me quita tiempo”. Siempre habrá razones para no escribir tests.
Y, por el contrario, siempre habrá buenas razones para escribir tests.
- Los tests siempre harán tu código más fiable aumentando tu certeza de que lo que estás programando, efectivamente, cumple su propósito.
- Contar con tests te ayudará a saber si alguna modificación introducida posteriormente rompe alguna otra funcionalidad de tu aplicación.
- Evitar el “titánico” esfuerzo de mantenimiento de código que suele haber cuando no hay tests. El tiempo es dinero.
En este primer artículo desarrollaré un poco la parte teórica y ciertos lineamientos a tener en cuenta al momento de querer introducir tests en una aplicación con código legacy.
¿Qué puedo hacer para hacer mi código “testeable”?
Aunque en la vida uno vive escuchando de cuando en cuando acerca de los tests y lo impresionante que son, creo que pocos realmente te orientan a hallar un proceso para hacer tu código testeable.
Partamos por algo básico y que seguramente ya han visto antes “la pirámide de los tests”.
La “Pirámide de los Tests_” es una metáfora que nos hace lleva a agrupar las pruebas de software en bloques de diferente granularidad. También, nos brinda una idea de cuántas pruebas deberíamos tener en dichos grupos. A pesar de que el concepto de la Pirámide de los_ Tests _ha estado presente por buen tiempo, los equipos siguen entrampados en cómo ponerlo en práctica. — _The Practical Test Pyramid (traducción libre)
Para no entrar en mucha discusión respecto a la pirámide, la figura se puede interpretar de la siguiente manera:
_Unit Tests — _la base está conformada por pruebas unitarias; estas son menos costosas y si tuvieramos que priorizar el esfuerzo para escribir pruebas debería ser en este bloque, principalmente, porque no hay una dependencia fuerte con frameworks y bibliotecas de código.
Service Tests—_el bloque del medio no se refiere solo a probar servicios; en realidad implica probar la integración entre componentes y las diferentes capas de nuestra aplicación. La priorización en esfuerzo es media, lo cual implica que el desarrollo de estas pruebas cuenta con una menor prioridad que los _unit tests.
_User Interface Tests — _finalmente, en la cúspide de nuestra pirámide se encuentran las pruebas de UI. El desarrollo de pruebas de UI guarda consigo cierta complejidad dependiendo de la aplicación que estemos probando. Proporcionalmente hablando, en muchos casos la cantidad de pruebas será menor que la de las otras capas.
Partamos con los “Unit Tests”
Un buen punto de inicio es comenzar a escribir unit tests (pruebas unitarias). Estas pruebas tienen como objetivo verificar el comportamiento de las funciones y la lógica de nuestras unidades de programación, las clases.
Como hablamos antes, se suele asumir que todos saben cómo hacerlas y, por ello, no se suele marcar un camino para llegar a ellas, pero en esta ocasión vamos a trazar algunos lineamientos y, posteriormente, lo traduciremos en una serie de tareas a seguir.
- Las funciones y clases como mínimo deben cumplir con algunos de los principios SOLID: The Single Responsibility (Responsabilidad simple) que nos menciona que una clase debería tomar responsabilidad sobre una sola parte de nuestra aplicación; Interface Segregation (Segregación de interfaces) que implica que varias funcionalidades podrían estar definidas en varias abstracciones en lugar de una sola y, Dependency Inversion (Inversión de dependencia) que implica evitar el acoplamiento entre clases a través de generar dependencias sobre abstracciones.
- Cuando una clase o función no cumpla con lo anterior, será una buena razón para comenzar a refactorizar, en un principio no haremos grandes cambios al diseño de la aplicación sino que comenzaremos a dividir funciones con múltiples responsabilidades en funciones de una sola responsabilidad. De ese modo separaremos nuestra lógica en funciones más pequeñas o incluso las terminaremos moviendo hacia otras clases.
- Nuestros unit tests seguirán la estructura Given-When-Then, una estructura que tomaremos prestada de BDD y que se traduciría de la siguiente manera: “Dado (Given) un estado de nuestro componente o Dada alguna condición; Cuando (When) una acción se realiza o se invoca a una función con un conjunto de parámetros; Entonces (Then) deberíamos obtener un resultado esperado”.
- Si hicimos bien la refactorización señalada en el punto 2, podremos mockear cualquier dependencia de la clase que queramos probar. Cuando me refiero a mockear, me refiero a que cualquier dependencia las podemos inflar o crear haciendo uso de una abstracción en conjunto con una concreción propia.
Bueno, teniendo esto en mente, en el siguiente artículo traduciré estos lineamientos a un ejercicio más práctico.
Referencias
- The Practical Test Pyramid — https://martinfowler.com/articles/practical-test-pyramid.html
- GivenWhenThen — https://martinfowler.com/bliki/GivenWhenThen.html
- SOLID Principles: Explanation and examples — https://itnext.io/solid-principles-explanation-and-examples-715b975dcad4
Top comments (0)