Probar software y facilitar su realización ha sido un interés para mí durante algunos años. Este interés ahora se centra más en las aplicaciones frontend que antes. Más específicamente, en las aplicaciones reactjs. Hace ya unos meses que me he sumergido en un código reactjs de entre cinco y diez años que me está proporcionando conocimientos y desafíos.
Personalmente, llevo algunos años desarrollando código en reactjs. Uno de los primeros proyectos de código abierto que he compartido se llama testable . Se lanzó en 2020 aproximadamente y, desde entonces, he dedicado parte de mi camino de aprendizaje a reactjs.
Recientemente, he estado trabajando en otros proyectos de código abierto que se centran en la parte de testabilidad de las cosas, como json-tool y text-tool ; ambas aplicaciones son de código abierto y se implementan en Snapcraft. Además de esas, con frecuencia realizo experimentos en un repositorio llamado reactjs-playground . Es el lugar donde experimento con las características de reactjs y dedico mis horas de aprendizaje a ello. La experiencia que he adquirido en esos años en proyectos de código cerrado y proyectos de código abierto me dio una base que me permite identificar algunas dificultades y ventajas comunes.
Los desarrolladores que se suman al patrón y las herramientas de ReactJS se dan cuenta de que los bloques de construcción que ofrece la biblioteca son fáciles de entender y componer. El concepto más abstracto de un componente se puede utilizar para componer interfaces de usuario rápidamente. Sin embargo, también puede ser una fuente de errores en lo que respecta a la estructura de la jerarquía de un componente. La descomposición de sistemas es un tema que se ha estudiado durante años.
El tamaño de la unidad de abstracción en el software también ha sido objeto de debate; algunos sostienen que los métodos y las clases deberían tener una cantidad pequeña de líneas, mientras que otros prefieren tener una pieza más grande y bien estructurada. Como en cualquier proyecto de software, independientemente del tamaño, tengo la intención de preferir el tamaño que proporcione más contexto y produzca menos fricción en la carga cognitiva mientras se lee el código.
Coincidencia o no, en mi experiencia, considero que esto es posible cuando tengo límites bien definidos para el fragmento de código en el que estoy trabajando junto con el contexto empresarial. Aún no he encontrado el número mágico para eso, sin embargo, mi medida se convirtió en la cantidad de saltos que tengo que hacer entre archivos para comprender lo que necesito hacer.
Cuantos más saltos tengo que hacer, más necesito retener en mi cabeza el contexto y la información; esto se vuelve difícil mientras se mantiene la base de código. Descomponer esos componentes es un desafío, sin embargo, se debe tener en cuenta para un mejor mantenimiento de la base de código.
Para el desarrollo profesional, el estado global es un requisito básico; para las aplicaciones de ReactJS, no es diferente. Los usuarios de la aplicación pueden detectar fácilmente el estado global. Si estás comprando algo y agregas el producto a tu carrito, puedes ver la cantidad de artículos que has agregado; este es el estado global.
En las aplicaciones de Reactjs, el paquete de gestión de estado global redux, que antes se usaba ampliamente , ahora ha disminuido su uso en favor de contextos más pequeños en torno a los límites de la aplicación. Sin embargo, la comunidad ha compartido diferentes opiniones sobre la necesidad de usar redux para la gestión de estado global; esto dio lugar a algunos blogs sobre el tema:
A pesar de la resistencia de la comunidad, react-redux, que es una biblioteca utilizada para vincular redux a los componentes de reactjs, ha tenido una creciente adopción en los últimos cinco años según npm trends . El 1 de febrero de 2025, las descargas que se muestran en npm para redux son 6.752.764.
Si te preguntas cuál es la alternativa a eso, la respuesta es el contexto de Reactjs y los ganchos con consultas.
Para las aplicaciones que dependen del paquete redux, es un desafío en sí mismo salirse con la suya. El estado global es una de las dependencias que reducen la capacidad de prueba de la aplicación. En mi experiencia, el contexto global requerido a menudo viene acompañado de una mayor complejidad del conocimiento del dominio. Si bien es posible que desee probar solo un componente en particular o una parte de su aplicación, no podrá hacerlo sin compartir las dependencias globales que requiere esta parte.
La adopción de ReactJS para aplicaciones empresariales, al momento de escribir este artículo, ha sido una decisión correcta en términos de mantenibilidad (a pesar del momento crítico que atravesó Facebook cuando cambió el modelo de licencias de bibliotecas). ReactJS brinda compatibilidad con versiones anteriores para API que ya no se utilizan, lo que hace que la adopción a largo plazo sea una de las principales ventajas.
Como si los componentes no fueran suficientes como concepto abstracto, los contextos también son una de las ventajas que proporciona reactjs en lo que respecta a las pruebas. Ahondaré más en este tema en una publicación de blog dedicada a las pruebas de contexto de reactjs . El nivel de encapsulación que proporciona el contexto de reactjs es uno de los beneficios clave para adaptar las pruebas y permitir la refactorización en las bases de código de reactjs.
Como la descomposición de componentes y la lógica de negocios son un desafío, los contextos son las herramientas que permiten una mejor reestructuración del código. En la parte de testabilidad, esto también es una ventaja, ya que es un mecanismo que permite reducir la cantidad de pruebas dobles utilizadas, lo que conduce a un código más testeable.
Con esta lista hasta ahora, es de esperar que la parte de pruebas haya quedado clara, ya que depende de cómo esté estructurado el código fuente y de cómo se hayan organizado los límites de la aplicación. Sin embargo, por mucho que el código de pruebas dependa del código de producción, los dobles de prueba pueden utilizarse para ayudar a delimitar el alcance. El proceso de pensamiento para adaptar las pruebas se compone de unos pocos pasos simples.
2.1 ¿Pasó la prueba?
2.1.1 - Yes → Check if the feedback is correct 2.1.2 - No → Provide the dependency without questioning
Tenga en cuenta que: el enfoque descrito aquí es similar a lo que se describe como la prueba de caracterización descrita por Michael Feathers en el
Libro Trabajar eficazmente con código heredado.
La llegada de los LLM también puede proporcionar información valiosa a la hora de escribir las primeras pruebas y adaptar las bases de código sin ninguna prueba. Cada paso de esta estrategia está pensado para ser iterativo; también es posible combinar diferentes estilos de TDD. El enfoque propuesto no consiste en dejar de hacer todo y adaptar todos los casos de prueba posibles y todos los problemas posibles que tenga la base de código, es un juego de rompecabezas. Cada funcionalidad probada es una pieza que encaja en el rompecabezas.
Se sugiere el mismo enfoque para la refactorización. Los desarrolladores deberían poder refactorizar constantemente un fragmento de código. No es un proyecto diferente y no está destinado a dedicarse únicamente a ello. Su objetivo principal es mejorar la base de código de lo que era. De manera iterativa. Hay algunos aspectos que se pueden utilizar para adaptar las pruebas en bases de código que no tienen ninguna:
En el centro de este enfoque se encuentra el proceso de aprendizaje; se tiene en cuenta cada paso del aprendizaje del código base.
Copilot se puede utilizar para automatizar la primera prueba exploratoria descrita en la sección anterior; siguiendo tres reglas, puede comenzar a identificar dependencias y escribir un conjunto de pruebas integrales que se utilizarán como base.
Tomemos como ejemplo Testable . Es una aplicación que se escribió hace más de cinco años en ReactJS y utiliza Enzyme para realizar pruebas. Para el estándar actual, la biblioteca que tomó el control es vitest o jest junto con la biblioteca de pruebas. Para adaptar los casos de prueba a Testable y aprovechar las ventajas de los LLM, podemos utilizar Copilot para que nos ayude con el trabajo pesado.
Testable está compuesto por una experiencia gamificada que cuenta con estados, niveles y progresión del usuario a través de diferentes desafíos. El componente descrito en la siguiente imagen es el componente utilizado para mostrar diálogos y navegar hacia adelante en el historial de la experiencia. Utilizando Copilot para vscode, le pedí que escribiera una prueba con el código de producción que nos interesa:
Una vez que carga la pregunta y analiza el código, proporciona la respuesta.
Junto con la respuesta, Copilot notó que aún no estoy usando una biblioteca de pruebas y me sugiere que lo haga. Después de eso, se muestra el caso de prueba generado. Si se ejecuta el caso de prueba como se muestra, la prueba no se aprueba y se marca en rojo.
Este punto es importante porque muestra que se deben tener en cuenta configuraciones adicionales y que Copilot no pudo resolverlas. Son las siguientes:
El flujo descrito anteriormente en esta sección también se aplica aquí. El ciclo de retroalimentación ahora consiste en comenzar a solucionar esos problemas y ejecutar las pruebas hasta que se complete el ciclo TDD.