martes, 28 de enero de 2020

Capítulo 28: el límite de prueba - Clean Arquitecture


Sí, eso es verdad: los test son parte del sistema y ellos participan en la arquitectura simplemente como cada otra parte del sistema lo hace. En algunas ocasiones, esa participación es bastante normal, en otras esta puede ser única.

Pruebas como un sistema de componentes

Hay mucha confusión con las pruebas. ¿Son parte del sistema? ¿Están fuera del sistema? ¿Cuales tipos de pruebas hay? ¿Son las pruebas unitarias y las pruebas de integración cosas diferentes? ¿Que hay sobre los test de aceptación, funcionales, pruebas Cucumber, TDD, BDD, prueba componentes, etc?

Este no es el objetivo de este libro entrar en ese debate y afortunadamente tampoco es necesario. Desde un punto de vista arquitectural, todas las pruebas son lo mismo. Ya sean pequeñas pruebas creadas por TDD, Cucumber, SpecFlow, etc. Todas son arquitectónicamente diferentes.

Las pruebas, por su naturaleza, siguen la regla de la dependencia. Estas son muy detalladas y concretas. Siempre depende hacía adentro el código que se está probando. En realidad se puede pensar en las pruebas como el círculo más externo en la arquitectura. Nada dependen de las pruebas dentro del sistema y las pruebas siempre dependen de los componentes del sistema. 

Las pruebas también son desplegables independientemente. En realidad, la mayoría del tiempo estas se despliegan en sistemas de pruebas más que en sistemas de producción. Así que, incluso en sistemas donde el despliegue independiente no es necesario, las pruebas todavía estarán desplegadas independientemente.

Las pruebas son los componentes del sistema más aislados. Ellas no son necesarias para la operación del sistema. Los usuarios no dependen de ellas. Su papel es dar soporte al desarrollo y no a la operación. Sin embargo, no son menos componentes del sistema que cualquier otro. De hecho, en muchos sentidos representan el modelo que todos los demás componentes del sistema deberían seguir.

Diseño para la testabilidad

El aislamiento extremo de los test combinados con el hecho de que ellos no se suelen desplegar, a menudo lleva a los desarrolladores a pensar que las pruebas caen fuera del diseño del sistema. Esto es un punto de vista catastrófico. Las pruebas que no están bien integrados en el diseño del sistema tienden a ser frágiles y ellos hacen el sistema rígido y difícil de cambiar.

Este problema se debe al acoplamiento. Las pruebas están fuertemente acopladas al sistema y deben cambiar con el sistema. Incluso el cambio más trivial a un componente del sistema puede causar que se rompan muchas pruebas que estén acopladas o requieran cambios estas pruebas.

La situación puede volverse peliaguda. Los cambios a componentes comunes del sistema pueden causar que decenas, cientos o miles de pruebas dejen de estar en verde. Esto se conoce como el «Fragile Tests Problem».

No es difícil ver como esto sucede. Imagina por ejemplo una batería de pruebas que usan una GUI para verificar las reglas de negocio. Dichas pruebas pueden empezar en la pantalla de inicio de sesión y el navegar a través de la estructura de páginas hasta que ellos puedan verificar una regla de negocio particular. Cualquier cambio a la página de inicio de sesión o a la estructura de navegación puede causar que un número sustancial de pruebas se rompan.

Las pruebas frágiles a menudo tienen el perversos efecto de hacer el sistema rígido. Cuando los desarrolladores realizan estos simples cambios al sistema se producen fallos de pruebas de forma masiva, así que ellos se puede resistir a hacer aquellos cambios. Por ejemplo, imagina la conversación entre el equipo de desarrollo y el equipo de marketing que solicita un simple cambio en la estructura de navegación que éste causara que se rompieran mil pruebas.

La solución es diseñar para poder probar el sistema. La primera regla del diseño de software, ya sea por probar el sistema o por cualquier otro motivo, es siempre la misma: No dependas de cosas volátiles. Las GUI son volátiles. Las baterías de pruebas que operan el sistema a través de la GUI DEBEN ser frágiles. Por lo tanto, diseña el sistema, y por tanto las pruebas, de manera que las reglas de negocio se puedan verificar sin usar la GUI.

La API de pruebas

La forma de lograr este objetivo es crear una API específica que las pruebas puedan usar para verificar todas las reglas de negocio. Esta API debería tener super poderes que permitan a las pruebas evitar restricciones de seguridad, eviten recursos con un alto coste (como bases de datos) y fuercen al sistema en un estado particular verificable. Esta API será un super conjunto del conjunto de interactores y adaptadores de interface que se usan por la interfaz de usuario.

El propósito de la API de pruebas es desacoplar las pruebas de la aplicación. Este desacoplamiento abarca más que simplemente desadjuntar los de la UI: el objetivo es desacoplar la estructura de pruebas de la estructura de la aplicación.

Acoplamiento estructural

El acoplamiento estructural es uno de las más fuertes e insidiosos formas de acoplamiento de pruebas. Imagina una batería de pruebas que tiene una clase de pruebas por cada clase en producción y un conjunto de métodos por cada método en producción. Tal conjunto de pruebas está profundamente acoplada a la estructura de la aplicación.

Cuando uno cambia uno de aquellos método de producción o clases también deben cambiar un gran número de pruebas. Consecuentemente, las pruebas son frágiles y estas hacen el código de producción rígido.

El rol de la API de pruebas es ocultar la estructura de la aplicación de los pruebas. Esto permite al código de producción que se pueda refactorizar y evolucionen de maneras que no afecten a las pruebas. Esto también permite refactorizar las pruebas y evolucionen de manera que no afecten al código de producción.

Esta separación de evolución es necesaria porque a medida que el tiempo pasa, las pruebas tienen a convertirse más concretas y específicas. Por el contrario, el código de producción tiende a convertirse más abstracto y general. El fuerte acoplamiento estructural previene, o al menos impide, esta evolución necesaria y previene el código de producción  de ser tan general y flexible como este debería.

Seguridad

Podría ser muy peligroso si los super poderes de la API de pruebas se llegaran a desplegar en los sistemas de producción. Si esto es una preocupación, entonces la API de prueba y las partes más peligrosas de implementación deberían mantener en un componente separado independiente desplegable.

Conclusión

Las pruebas no están fuera del sistema. Estas son parte del sistema que debe estar bien diseñado si ellos están para proveer los beneficios deseados de estabilidad y regresión. Las pruebas que no están diseñadas como parte del sistema tienden a ser frágiles y difíciles de mantener. Tales pruebas se descartan a menudo porque son difíciles de mantener. 

No hay comentarios:

Publicar un comentario