Campaña CONTRA EL TESTING | Tu código NO NECESITA TESTS

¿Por qué no escriben tests los programadores? Todos hablan bien acerca del testing de software, pero nadie lo pone en práctica. ¿Por qué?

Categorías
Testing

¿De verdad necesitas probar tu código?

¿Por qué todo el mundo habla maravillas sobre el testing, pero la mayoría de los programadores no escriben tests?

El testing es como El traje nuevo del emperador: todos lo elogian para no parecer ignorantes, pero nadie es capaz de verlo. ¿Por qué? ¿No hay ningún programador con criterio propio que se atreva a poner en duda el valor del testing en un proyecto de desarrollo de software? 

Aunque los tests son útiles para garantizar que tu código funciona correctamente, a su vez añaden más código que tendremos que mantener y que también puede tener errores. Así que es importante escribirlos sólo cuando sean necesarios.

¿Por qué no escribimos tests?

Si ya has tenido experiencias laborales como programador, sabrás que en la mayoría de proyectos del sector no se trabaja con tests. Ni en el frontend, ni en el backend, ni en aplicaciones móviles, ni en desarrollo de videojuegos. En ningún sitio y esto es una realidad.

La pregunta es: si el testing sólo aporta cosas positivas y cada vez que hablamos de él se nos llena la boca de buenas palabras, ¿por qué es tan poco frecuente ver esta práctica implementada en las empresas?

Y la respuesta corta es: porque nadie quiere hacerlo. Ni la empresa, ni los programadores, ni los clientes.

El tiempo es dinero: las empresas no priorizan los tests

En el mundo empresarial, hay una verdad absoluta: el tiempo es dinero.

Las empresas están más preocupadas por cumplir con los plazos de entrega y sacar nuevas funcionalidades que por garantizar que cada línea de código esté perfectamente testeada. El testing, aunque es útil, no se percibe como una prioridad inmediata.

Cerdo de Swooner Crooner apurado

¿Y por qué no es prioridad? Porque los beneficios del testing son a largo plazo, mientras que las exigencias de los clientes se tienen que atender lo antes posible. Si el cliente quiere una nueva funcionalidad para “ayer”, ¿qué crees que va a decidir tu jefe? Exacto, olvídate de los tests y ponte a programar esa feature para entregarla a tiempo.

Además, el testing no es algo que puedas mostrar en un informe o en una demo al cliente. Un cliente quiere ver pantallas, gráficos, botones que funcionan? pero no le importa que detrás haya una suite de tests. Para ellos, los tests no son algo tangible. Si todo “parece funcionar”, la mayoría de los clientes preferirán asumir el riesgo y seguir adelante, ya que al fin y al cabo son ellos los que pagan el desarrollo.

A los programadores no les gusta escribir tests

Vamos a ser honestos: programar tests no es sexy. No te genera la misma emoción que desarrollar nuevas funcionalidades, no te da esa sensación de logro instantáneo que sientes al ver cómo algo nuevo funciona por primera vez. Es tedioso, repetitivo y, en el peor de los casos, frustrante, porque puedes pasarte horas arreglando un test que no para de fallar sin razón aparente.

Para más inri, escribir tests correctamente requiere disciplina y paciencia, cualidades que cuesta encontrar en los equipos de desarrollo. Muchos programadores prefieren confiar en sus propios instintos, pensando: “si lo he hecho yo, seguro que está bien”. Pero la realidad es que hasta el mejor programador comete errores. Así que imagínate cómo serán los tests de un desarrollador que lleva años quemado con la empresa y trabaja a desgana desde su casa.

En general, es difícil encontrar buenos profesionales comprometidos con su trabajo en el sector de la programación. Y si sumas esa falta de profesionalidad a la baja prioridad que las empresas le dan a los tests, por los motivos que ya hemos mencionado, pues tenemos el cóctel perfecto para que nadie haga ni un intento de implementar el testing.

Mantener los tests no es gratis

El código de los tests también es código, y ese código hay que mantenerlo.

Los tests hay que actualizarlos cada vez que se modifica el código que están probando. En un entorno donde los requisitos cambian constantemente, los tests se convierten en una carga adicional que nadie quiere asumir.

En muchos casos, los tests terminan rompiéndose o quedando obsoletos. Esto lleva a los equipos a ignorarlos o incluso eliminarlos por completo.

Suite de tests fallando en consola

En el caso de las startups, que se practique el testing es aún menos probable. Cuando el objetivo principal es lanzar un producto mínimo viable lo antes posible, nadie quiere “perder el tiempo” escribiendo tests. La prioridad es demostrar que la idea funciona, no que el código sea impecable.

Para las startups, el testing es un lujo que no pueden permitirse. Ya que probablemente están subsistiendo con rondas de inversión o con los ingresos de sus primeros clientes.

En el mundo real, los proyectos no triunfan por tener una suite de tests con un code coverage del 100%, sino por entregar valor al cliente a tiempo. Las empresas no pagan por tests; pagan por soluciones. Si tu obsesión por testearlo todo está frenando al equipo, generando frustración y desviando recursos, entonces eres parte del problema, no de la solución.

Mitos sobre el testing

No creo que el testing sea siempre positivo y algunas de las cualidades que se le atribuyen son más mitos que verdades. Así que vamos a desmantelar estos mantras tan repetidos entre los programadores.

Si no hay tests, el código no es profesional

Esta afirmación no solo es falsa, sino también peligrosa. Profesionalismo significa cumplir con las necesidades del cliente, o del producto que estás desarrollando, de manera eficiente y entregando software de calidad. Entendiendo por calidad un código funcional y lo suficientemente limpio para ser mantenido y extendido de forma cómoda.

Tener tests no es mejor que no tenerlos, no tengo miedo a decirlo. De hecho, hay muchísimos proyectos exitosos y admirables técnicamente, en los que el enfoque no es tener una suite amplia de tests, sino una arquitectura que nos permite iterar rápido y responder al cambio.

Un mono programando con un ordenador

Algunos programadores confunden tener muchos tests con escribir buen código, pero la verdad es que puedes tener una suite de tests enorme y aun así desarrollar software mediocre o mal diseñado.

Ser un profesional es saber cuándo los tests son necesarios y cuándo son una pérdida de tiempo.

Los tests garantizan que no hay bugs

Que los tests garantizan que el código no tiene bugs, es una mentira a medias.

El principal objetivo de los tests es ese, probar que el código funciona correctamente. Pero, ¿qué pasa si son los tests los que tienen errores?

Cobertura de código perfecta en repositorio

Al fin y al cabo, un test es un código que comprueba que otro código hace lo que tiene que hacer. Y ese código lo escribimos los programadores, por lo que es posible que introduzcamos errores o que no comprobemos todos los casos posibles.

Que un test tenga errores es un problema porque crea un falso positivo en nuestra batería de pruebas. ¿Para qué voy a probar manualmente la API del backend que he escrito, por ejemplo, si el test no detecta ningún problema?

Podemos entender los tests como una red de seguridad para nuestro código, pero sólo si los tests están bien escritos. Y nadie nos puede garantizar que no hemos cometido ningún error en los tests, así que el riesgo de tener bugs siempre existe.

Los tests son una inversión, no un gasto

Si escribes tests para cada línea de código, sin distinguir qué partes del sistema son críticas y cuáles no, estás desperdiciando tiempo y recursos.

Que los tests son una inversión, no solo un gasto, es una frase que queda bien como argumento de los fanáticos del testing. Pero el código cambia constantemente: lo que hoy parece una buena inversión en tests, mañana podría convertirse en deuda técnica cuando esos tests empiecen a romperse al refactorizar el código o tras un cambio de requerimientos.

Homer bailando con chicas vestidas de dinero

No se trata de evitar los tests, sino de utilizarlos de forma inteligente. En lugar de cubrir todo, enfócate en las partes más críticas del sistema, donde un fallo podría tener el mayor impacto. De lo contrario, acumularás un coste continuo que casi siempre superará los beneficios que esperabas.

No te engañes, los tests no te están aportando tanto, pero te apetece presumir tú insignia de code coverage en el repositorio.

Estos son algunos de los enunciados sobre el testing que están ampliamente aceptados y que a menudo se utilizan como falacias, sobre todo desde programadores con más experiencia hacia otros perfiles más junior.

¿Cuándo hacer tests?

Los tests no son la solución a todos tus problemas, pero desde luego son herramientas útiles en algunos escenarios.

Esta es una campaña contra el testing, pero os confieso que yo también utilizo tests en algunos casos.

Librerías o paquetes de código

Cuando estás programando una librería para utilizarla en varios proyectos o para publicarla como open source para la comunidad, vale la pena escribir tests.

Material UI - Librería de componentes de React

Si el código que estás escribiendo lo va a utilizar más gente, o vas a utilizarlo en más de un proyecto, lo más inteligente es acompañarlo de tests que comprueben explícitamente que funciona para todos los casos.

En un proyecto de React, no merece la pena escribir un test para cada componente, pero si desarrollas una librería de componentes como Material UI, escribirás un test para el Button, uno para el Checkbox, uno para el Input?

Funcionalidades muy complejas

Si tienes que programar una funcionalidad compleja, los tests no solo son recomendables, si no que a veces son totalmente necesarios.

En programación, la estrategia para encontrar la solución a un gran problema, es subdividirlo en tareas más pequeñas. Estas pequeñas tareas, pueden ser resueltas con funciones sueltas que son muy fáciles de testear con tests unitarios.

Hace poco desarrollé un sistema de inyección de dependencias genérico llamado Inversiland, como el de Angular o Nest, a partir de Inversify. De primeras suena muy complicado, pero si divides en funciones el comportamiento y lo acompañas de tests, puedes desarrollarlo sin demasiados inconvenientes y probar a trozos que el sistema funciona correctamente.

Repositorio de Inversiland - InversifyJS framework

En ese caso, me vi obligado a trabajar con tests, no era capaz de mantener en la cabeza todos los detalles del sistema que estaba desarrollando. Échale un vistazo al repositorio en GitHub para ver una estrategia de testing implementada de forma adecuada.

Implementaciones críticas de un sistema

Hay partes de un sistema, que son claramente más críticas que otras.

Por ejemplo, en una tienda online, el registro de usuarios, inicio de sesión y flujo de pago, son los puntos más delicados. Si tuviéramos que escribir tests para esta aplicación, empezaríamos por estas tres funcionalidades, para garantizar que el usuario puede registrarse, iniciar sesión y completar una compra sin problemas.

Escribir pruebas para estas tres funcionalidades no te va a suponer un gran esfuerzo, pero te va a aportar mucha seguridad. Es cuestión de trabajar de forma inteligente.

Dominio de la aplicación / Lógica de negocio

Si la lógica de negocio, o dominio de nuestra aplicación, está aislado del resto del código, es una buena idea acompañarlo de tests unitarios.

Volviendo al ejemplo de la tienda online, si queremos asegurar que el precio de los productos que se muestra a los usuarios es siempre correcto, después de aplicar ofertas o añadir complementos, escribiremos un test para la fórmula que calcula el precio final.

Test unitario para un modelo del dominio de una aplicación

Teniendo este cálculo en una función separada, acompañada con su test, podremos mostrar precios, en cualquier parte de nuestra aplicación, de forma segura.

Fuera de esos casos, el testing puede convertirse más en una carga que en un beneficio para el proyecto.

Alternativas al testing “tradicional”

El testing es una herramienta que podemos utilizar para afianzar partes del proyecto como las que hemos mencionado, pero no siempre podemos aplicarlo. Ya sea porque el código de la aplicación no está lo suficientemente desacoplado; o porque los tests no se han valorado en la planificación del proyecto.

Por suerte, existen otras herramientas, que podemos combinar entre sí, y se adecúan más a lo que necesitamos en determinados escenarios para filtrar la mayor cantidad de errores posibles.

Revisiones de código

Las revisiones de código son una buena forma de validar manualmente que no se están introduciendo nuevos errores en la aplicación.

La mayoría de sistemas de control de versiones, como GitHub, ofrecen la opción de asignar revisores a los cambios que subimos al repositorio de código. De esta forma, cada vez que cambiemos una línea de código, otro compañero de trabajo tendrá que darnos su visto bueno o dejar los comentarios con las modificaciones que deberíamos hacer.

Revisiones de código en Github

Por supuesto, las revisiones de código no son infalibles, pero son muy utilizadas en el mundo empresarial, ya que viene muy bien que otra persona le eche un último vistazo al código antes de subir algo nuevo producción.

Programación en pareja

Otra metodología de trabajo que nos ayudará a evitar despistes mientras programamos es el pair programming o programación en pareja.

Consiste en programar funcionalidades de dos en dos desarrolladores, uno controla el ordenador y otro va dando las instrucciones. De forma similar a lo que ocurre con las revisiones de código, cuatro ojos ven más que dos. Así que lo que se le escape a uno, seguramente al otro no.

No creo que sea una técnica que podamos usar durante toda la jornada laboral, por motivos obvios de productividad, pero sí que es muy efectiva para partes concretas del desarrollo o en procesos de depuración, cuando estamos buscando un error.

Monitoreo de errores

Las empresas que no escriben tests, en lugar de destinar sus recursos en la prevención de errores, se centran en mitigarlos rápidamente cuando aparezcan.

Sentry - Monitoreo de errores

Es muy común implementar en el proyecto un sistema de monitoreo de errores en producción con herramientas como Sentry, que notifica a los desarrolladores cada vez que se produce un error en la aplicación.

Estas alertas incluyen información tan útil como la línea en la que se ha producido el error, la hora o el navegador y dispositivo en el que ha tenido lugar.

Todas estas técnicas son combinables entre sí y juntas forman una buena alternativa al testing tradicional. Que como ya hemos dicho, no siempre es posible implementar.

Conclusión sobre el testing

El testing no es ni una panacea, ni un enemigo. Es una herramienta que, como cualquier otra, debe usarse con criterio. Su utilidad depende del contexto, los objetivos del proyecto y los recursos disponibles.

No necesitas tests para demostrar que eres un buen programador. Lo que necesitas es saber cuándo son necesarios y cuándo no.

Personajes de la serie Silicon Valley probando código

Mi recomendación es clara: no escribas tests porque “hay que hacerlo”, ni te obsesiones con un code coverage perfecto. Escribe tests cuando realmente aporten valor al proyecto: en funcionalidades críticas, en cálculos complejos o en código que será reutilizado muchas veces.

Y si no puedes implementar testing tradicional, recuerda que hay otras alternativas igualmente válidas: revisiones de código, programación en pareja o tracking de errores. Todas ellas pueden ayudarte a reducir riesgos sin lastrar el desarrollo con la escritura y mantenimiento de tests.

Lo importante no es seguir un dogma, sino enfocarte en entregar software funcional. Porque al final del día, a nadie le importa cuántos tests has escrito, sino el valor que aportas al cliente y lo productivo que seas para tu empresa.

Carlos Sala Samper

Handmade software.