Finalmente me siento cómodo al aplicar TDD

Explico el mindset a seguir para poder aplicar TDD sin perder la cabeza

hace 4 meses   •   5 min de lectura

Por Andrés Tuñón
Tabla de contenidos

Llevo meses y podría que decir años estudiando acerca de pruebas unitarias y jamás me había sentido tan cómodo al aplicar TDD. Aquí mis anteriores artículos:

Unit Testing eficaz en frontend
Explico desde mi experiencia la eficacia de múltiples herramientas de unit testing y la más completa
El Unit Testing no debe ser tan difícil como parece
Te muestro algunos tips y mi perspectiva al probar una nueva funcionalidad.

Estos artículos los utilizaré como base para el actual:

  • En el primero toco la importancia, beneficios y herramientas como Jest y Storybook
  • En el segundo explico que las pruebas son más diseño y arquitectura del código, que la prueba en sí. No son automáticas, tienen un sentido y deben probar lo que quiere ver tu cliente.

Ejemplo: Botón de Inicio de Sesión con Twitch

Mi objetivo es lograr completar el flujo de autorización, el cliente debe ver el botón, que se abra una pestaña o popup para los permisos y al autorizar obtener un código.

La idea en mis pruebas serán asegurarme que se presentará el botón en pantalla, al darle click haga el intento de abrir el popup y al intentar cerrarse obtener el código.

Las primeras pruebas

Claramente como programador pruebo que se vea el botón y es bastante sencillo:

Así empezó el componente
Así empezó el componente

Luego probé que global.open fuera llamado para saber si iba a abrir el popup y llegué hasta ahí:

Probando el popup
Probando el popup

Diseño

El componente debe ser simple o presentacional; ya que si dejo la lógica de autorización dentro, el componente seria responsable de muchas cosas y me complica las pruebas.

El componente y sus props
El componente y sus props

Explicando un poco:

  1. twitchClientId es una llave de las variables de entorno
  2. onCode es un event handler para el momento que se cierra el popup con el código de autorización
  3. onClose es un event handler para el caso que se cierra el popup, digamos que sería el rechazo

Por dentro utiliza una librería que me ayuda a escuchar el momento que vuelve el código, cuando se cierra el popup y otros; solo no rehice la rueda.

Si se percatan el diseño de mi componente no empezó con esos props, las pruebas me forzaron a hacerlo así
  1. twitchClientId: Si obtenía la variable de entorno de twitchClientId, tendría que falsificar el process.env o configurarlo en jest
  2. onCode si la lógica al recibir el código la dejaba dentro, no me quiero imaginar la cantidad de dependencias a falsificar que estarían dentro.
  3. onClose si la lógica al cerrar la dejaba dentro, debía preocuparme falsificar el global.opener dentro del componente
Por eso el simple hecho de pensar en un ambiente de pruebas provoca un cambio importante en el diseño

Coverage

Gracias al coverage me aseguro que todo el código es revisado por las pruebas unitarias:

Primera prueba

Con todos los ajustes de diseño mencionados y las 2 pruebas básicas del principio queda así, hay unos detalles pero es bastante limpio y esta en un 90%:

Coverage de la primera prueba
Coverage de la primera prueba

El resto de pruebas

Luego de incluir unos ciertos detalles como la verificación de la variable de entorno, el caso de onCode y onClose en las pruebas:

Pruebas completas
Pruebas completas

Coverage con todos los casos

Coverage completo
Coverage completo

Ahora tocaré los puntos que aprendí de todo esto:

¿Cuál es el clásico problema al hacer TDD?

No tengo manera clara de relacionar lo que no está programado, por lo tanto me cuesta empezar. Justamente es algo que soluciona React Testing Library y su filosofía

Diversas Filosofías: React Testing Library vs Enzyme

Mientras que React Testing Library te permite probar lo que está en el DOM como si fuera E2E casi, Enzyme ayuda a probar la parte interna del componente, con sus props e implementaciones.

La desventaja de Enzyme es que revisa tanto, que el mínimo cambio rompe la prueba; provocando un dolor de cabeza al tener que mantener pruebas así.

¿Qué realmente me importa saber al probar?

  • Qué es lo que percibirá el cliente al ver y dar click al botón
  • ¿Se daño este comportamiento previo al cambiar algo?

Puede que exista un caso muy excepcional en donde quiera saber algo de la lógica, pero el frontend es muy visual, la gracia es como el usuario utiliza la pantalla.

En caso de revisar lógica, seguro lo aparto en alguna clase o función para separar sus dependencias.

¡Voilà!

Esa es la magia del TDD, te preocupas de lo que el cliente realmente va a recibir, lo mismo pasa para backend:

  1. Sabes las respuestas que debes obtener/enviar desde Insomnia/Postman. Aún no sabes cómo vas a lograrlo.
  2. Armas el código que necesitas para lograr eso
  3. Las pruebas finalmente pasan
  4. Ajustas el código y las pruebas para casos que quedaron rezagados

Hay librerías como node-mocks-http que tienen ese patrón o el servidor de pruebas de hapi.js

El truco es dejar las pruebas enfocadas en lo que se quiere lograr.

Referencias

Guiding Principles | Testing Library
[The more your tests resemble the way your software is used, the more
GitHub - mawrkus/js-unit-testing-guide: 📙 A guide to unit testing in Javascript
📙 A guide to unit testing in Javascript. Contribute to mawrkus/js-unit-testing-guide development by creating an account on GitHub.

Corre la voz

Sigue leyendo