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:
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:
Luego probé que global.open fuera llamado para saber si iba a abrir el popup y llegué hasta ahí:
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.
Explicando un poco:
- twitchClientId es una llave de las variables de entorno
- onCode es un event handler para el momento que se cierra el popup con el código de autorización
- 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í
- twitchClientId: Si obtenía la variable de entorno de twitchClientId, tendría que falsificar el process.env o configurarlo en jest
- 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.
- 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%:
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:
Coverage con todos los casos
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:
- Sabes las respuestas que debes obtener/enviar desde Insomnia/Postman. Aún no sabes cómo vas a lograrlo.
- Armas el código que necesitas para lograr eso
- Las pruebas finalmente pasan
- 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.