TDD and BDD - Pros and Сons - Redwerk

En los últimos 12 años, nuestra empresa ha realizado con éxito decenas de proyectos, tanto grandes como pequeños. Durante este tiempo, el proceso de desarrollo se ha revolucionado significativamente. Hace cinco años, el flujo de trabajo era el siguiente: primero escribíamos el código y luego, si teníamos tiempo suficiente, creábamos un conjunto de pruebas unitarias para el código existente. La cobertura de las pruebas para el código solía ser inferior al 50%. Como podemos ver ahora, este enfoque tenía una serie de deficiencias importantes. Antes, los desarrolladores a menudo no veían la incoherencia de la lógica, y el código que tenía errores se enviaba al servidor de pruebas. Cabe mencionar que este código de baja calidad nunca llegaba a producción, ya que la falta de pruebas asistidas por ordenador se compensaba con el trabajo de alta calidad del departamento de control de calidad. La revisión del código también fue de gran ayuda, ya que fue ejecutada únicamente por desarrolladores senior con décadas de experiencia en esta área. Los clientes siempre estaban contentos con el producto final que veían en producción, pero en el proceso los tickets a menudo no cumplían las expectativas del departamento de control de calidad y se devolvían a los desarrolladores. Mientras tanto, entró en vigor un nuevo requisito de código. En él se establecía que un alto porcentaje del código debía estar cubierto por pruebas unitarias. Teniendo en cuenta todo lo anterior, hace varios años nuestra empresa comenzó a implementar una serie de técnicas y utilidades de pruebas asistidas por ordenador como Selenium. Además, utilizamos herramientas de integración continua como Jenkins para casi todos los proyectos. En caso de que el código comprometido no pase las pruebas unitarias, no se desplegará en el servidor de pruebas, y todos los desarrolladores recibirán el informe correspondiente por correo electrónico. Así pues, analicemos detenidamente los diferentes métodos de pruebas asistidas por ordenador, sus pros y sus contras.
Unit testing and TDD

Pruebas unitarias y TDD Pros y Contras

Las pruebas unitarias son un método utilizado para probar bloques de código separados (clases, componentes) lógicos en los que se basan para funcionar. En realidad, las pruebas unitarias se ejecutan de forma automática y representan un pequeño bloque de código para probar la precisión de la salida esperada de un solo componente o de un conjunto de ellos. Estos componentes se prueban por separado de sus dependencias en una prueba de aislamiento: BD, almacenes, sistemas de archivo, redes, etc. El Desarrollo Dirigido por Pruebas (TDD) es una metodología de desarrollo basada en la escritura de pequeñas pruebas asistidas por ordenador para el código (pruebas unitarias). Como resultado de la utilización de esta metodología, obtenemos un conjunto completo de pruebas unitarias que pueden ejecutarse en cualquier momento cuando necesitemos comprobar si el código de la aplicación funciona correctamente. El TDD es utilizado casi universalmente por las empresas que utilizan métodos de desarrollo ágiles. Tenga en cuenta que en TDD escribimos primero la prueba (¡y no la realización en código!) Tras el primer lanzamiento, esta prueba no cumple todos los requisitos técnicos. Entonces el desarrollador tiene que implementar la funcionalidad mínima en el código para asegurarse de que la prueba se realiza con éxito. Después de esto, se lleva a cabo la refactorización y la mejora del código.

El lema corto del Desarrollo Dirigido por Pruebas puede sonar como “Rojo-Verde-Refactor”:

  • “Rojo”: crear una prueba unitaria y ejecutarla para ver si falla
  • “Verde”: implementar la lógica en un código para completar la prueba
  • “Refactorizar”: mejorar el código, para evitar duplicidades, mejorar la arquitectura para que la prueba se complete con éxito

Junto con las pruebas escritas antes de la ejecución de la lógica real en el código, TDD garantiza la máxima calidad de un producto y ayuda a todo el equipo de desarrollo a centrarse en la creación de un código sencillo y comprensible. Pero como las pruebas unitarias se centran en el concepto interno del código de la aplicación, a los desarrolladores externos les resultará difícil entender el concepto que hay detrás de la aplicación. Además, surgen nuevas dificultades con la evaluación del concepto y la cobertura del código, así como la calidad de las pruebas unitarias antes de las pruebas de integración. Por eso, toda la responsabilidad de las pruebas unitarias no recae en el departamento de control de calidad, sino en los desarrolladores, porque las pruebas unitarias manejan bloques de código de bajo nivel y requieren el conocimiento de la arquitectura del software de la aplicación. Además, los probadores no deberían trabajar con las pruebas unitarias dadas las iteraciones entre la creación de una prueba por parte de un desarrollador y la implementación para su superación con éxito.

Contras de TDD

  • algunos desarrolladores siguen considerando las pruebas como una completa pérdida de tiempo
  • la necesidad de crear código adicional en las pruebas aumenta el tiempo necesario para el desarrollo
  • las pruebas pueden implementarse fácilmente de forma errónea, comprobarán el trabajo de clases específicas y sus métodos, pero no el sistema en general

Ventajas de TDD

  • un conjunto de pruebas unitarias garantiza una retroalimentación constante sobre el funcionamiento de todos y cada uno de los elementos del sistema
  • las pruebas unitarias forman parte de un proyecto y no pueden quedar obsoletas, a diferencia de la documentación específica que se olvidará tarde o temprano
  • TDD requiere una clara comprensión de la lógica de funcionamiento del código, ya que sin una clara comprensión de los resultados esperados no se puede ejecutar la prueba
  • la calidad del código del proyecto crece, porque un desarrollador puede refactorizar el código en cualquier momento y comprobar la exactitud de su funcionamiento
  • el número de tickets devueltos por el QA a los desarrolladores disminuye, porque una parte de los errores en el código son comprobados por las pruebas unitarias
  • un conjunto de pruebas funciona como una red de seguridad porque durante la corrección de errores los desarrolladores crean pruebas para comprobar si los problemas se repiten o no. De este modo, la probabilidad de que aparezcan errores similares disminuye

Por último, no podemos dejar de mencionar dos métodos prácticos fundamentales que se aplican en las pruebas unitarias de integración de código: mocking y stubbing. Si busca estas palabras en un diccionario, verá que el sustantivo “mock” significa “hecho para imitar”. El mocking se utiliza sobre todo en las pruebas unitarias porque el objeto suele tener dependencias en forma de otros objetos complejos. Para aislar el comportamiento de un objeto probado, puede sustituir sus dependencias por mocks que simulan el comportamiento de las dependencias reales. Se trata de una técnica útil, ya que es poco práctico incluir la mayor parte de los objetos reales en la prueba unitaria. Resumiendo, mocking es un proceso de creación de objetos que simulan el comportamiento de dependencias de objetos reales. A veces, podemos ver el mocking como lo opuesto al stubbing. El stub es un tope. O, en otras palabras, un objeto simulado “mínimo” o vacío, que a menudo no tiene lógica ni comportamiento. Esto significa que el stub se crea para ayudar a que la prueba se ejecute con éxito. Esto permite a los desarrolladores comprobar si los objetos probados interactúan con el mock correctamente o lo utilizan.

Veamos un ejemplo:

A menudo necesitamos crear una DB falsa en las pruebas unitarias para probar un objeto que tiene DAO en las dependencias. Creando un stub, imitamos fácilmente la DB ya que creamos una estructura de datos para almacenar datos. Ahora podemos crear fácilmente DAO en forma de dependencia a un objeto probado del servicio que almacenará y eliminará entradas de un stub de base de datos falsa en memoria. Esto nos permitirá ejecutar una prueba con éxito. Pero antes, no podemos probar el comportamiento general de un objeto probado, en caso de que sólo haya 3 entradas con un conjunto específico de valores de campo en la DB. Para estos propósitos, un stub normal no es suficiente, por lo que necesitamos crear un mock y añadir datos específicos en él. Después especificaremos las aserciones en una prueba unitaria para comprobar el comportamiento del servicio en un caso de prueba con colecciones de datos específicas. También debemos señalar que el hecho de que todas las pruebas unitarias se realicen con éxito no significa que la aplicación funcione bien en general. Para comprobar el funcionamiento general de la aplicación se necesitan pruebas de un nivel superior (o pruebas de integración). Las pruebas funcionales (o de integración) consideran la lógica del proyecto como un único hilo funcional. Representan una interacción completa de objetos internos y externos (componentes) con el objetivo de lograr el comportamiento esperado de una aplicación probada. Las pruebas funcionales son pruebas de alto nivel, y si el código las supera con éxito, significa que la aplicación funciona bien. Un fallo, a su vez, significa que el código no proporciona la funcionalidad afirmada.
Behavior Driven Development advantages and disadvantages - Redwerk

Pros y contras del desarrollo impulsado por el comportamiento

El Desarrollo Dirigido por el Comportamiento (BDD) se basa en el TDD, pero el TDD se centra en los procesos internos del software y en la precisión del rendimiento del código (pruebas unitarias), mientras que el BDD sitúa los requisitos y el Valor de Negocio del software en la cima de las prioridades del software (pruebas de aceptación). Por su parte, las pruebas de aceptación suelen modelarse en función de las Historias de Usuario y los criterios de aceptación. Estas pruebas se describen normalmente con palabras sencillas para que las personas ajenas al sector informático (como los accionistas, los analistas de negocio, los ingenieros de control de calidad y los directores de proyecto) las entiendan mejor. BDD se asegura de que en el proceso de desarrollo del producto se creen pruebas en primer lugar. Estas primeras pruebas deben describir la funcionalidad esperada de un producto y el comportamiento del software. Luego, la realización de la funcionalidad del producto se ejecuta en función de estas pruebas. Es muy conveniente, por lo que utilizamos BDD para las pruebas de aceptación. A partir de esto, podemos suponer que BDD y TDD se complementan, ya que representan un enfoque diferente para resolver problemas similares. Como la gestión del desarrollo se realiza a través de una prueba, y en el proceso cada componente va “de rojo a verde”, lo que significa que al principio falla (no hay funcionalidad) pero luego sale adelante con éxito (su funcionalidad cumple con una especificación). Gracias a poner las Historias de Usuario en la cima de las prioridades mientras se componen las pruebas en BDD, el resultado final cumple con las expectativas del cliente, ya que es mejor escribir el modelo de comportamiento del usuario, sus acciones y las funcionalidades que podría necesitar antes de comenzar el desarrollo. Nuestro equipo utiliza Python Cucumber y Gherkin para escribir y ejecutar nuestras Historias de Usuario. Gherkin es un lenguaje legible para el negocio y de dominio específico. Se puede utilizar para describir el patrón de comportamiento del usuario en voz alta y clara, dividiéndolo en varios scripts. Cada script representa una historia de usuario independiente. Cada línea de dicho script es un requisito para el software que se asigna a la función que se ejecuta en el lenguaje Python. Gracias a este tipo de integración, podemos utilizar BDD para comprobar los aspectos funcionales de la experiencia del usuario con una aplicación de navegador. Por ejemplo, utilizamos la herramienta de automatización del navegador Selenium junto con los enlaces de Python para ejecutar nuestra prueba de aceptación de la interfaz de usuario desde Gherkin. Cada historia de usuario se comprueba en la última versión de una aplicación dentro del navegador, y Python envía instrucciones al navegador para la emulación de diferentes actividades del usuario (por ejemplo, clics), con la posterior comprobación del resultado de esta acción según un conjunto de criterios (mensaje de ejecución satisfactoria, error de validación, etc.) Como ya hemos mencionado anteriormente, BDD requiere la creación de un guión de acciones del usuario en primer lugar. Por eso, en primer lugar creamos un guión en el que describimos ejemplos de situaciones para cada uno de nuestros componentes. La creación del software se lleva a cabo en último lugar.

Este es el aspecto de nuestro ciclo de desarrollo:

  1. Escriba un script en Gherkin
  2. Ejecutar el script y darse cuenta de que nada funciona correctamente

      2.1 Identifique las situaciones en las que este script debe funcionar

      2.2 Empiece a comprobar estas situaciones y compruebe que nada funciona bien

      2.3 Identifique e implemente la funcionalidad mínima necesaria para que todos los ejemplos pasen la prueba

  3. Ejecute todo una vez más. En caso de nuevos errores, vuelva al punto 2.1
  4. El script funciona Empiece a crear un nuevo script que cubra la mayor parte de los requisitos

Dan Nort fue el primero en deletrear el enfoque BDD afirmando que este método está aquí para eliminar los problemas con el TDD

Los contras de BDD:

  • requiere una comprensión profunda de un mayor número de conceptos, lo que no permite recomendar BDD a un desarrollador junior antes de que comprenda completamente el concepto de TDD
  • al tratarse de un concepto, convertirlo en una práctica técnica o vincularlo a un conjunto de herramientas significa arruinarlo

Pros de BDD

  • el equipo de negocio y los desarrolladores demuestran un verdadero trabajo en equipo y permiten que las personas adecuadas discutan las cosas correctas en el momento adecuado
  • hace que el negocio justifique la prioridad de la funcionalidad, ya que muestra su valor real
  • permite a los equipos de desarrolladores centrarse en las funcionalidades priorizadas por el negocio gracias a un mejor entendimiento
  • los desarrolladores rara vez se pelearán con el equipo de negocio para escribir unas funcionalidades antes que otras
  • gracias a que el lenguaje utiliza una base de conocimientos común, el equipo de negocio y los desarrolladores trabajarán en un mismo proyecto y se mantendrán en la misma página al respecto
  • le ayuda a centrarse en las necesidades del usuario y en el comportamiento esperado en lugar de sumergirse en todos los detalles de la implementación de inmediato
  • puede ayudar a los equipos a centrarse específicamente en los detalles de la funcionalidad y a probar las cosas que son importantes en lugar de limitarse a crear pruebas para todo el código
  • requiere un crecimiento constante en la comprensión de los requisitos del producto, facilita el desarrollo de aplicaciones que cambian constantemente
  • hace que las personas colaboren estrechamente, especialmente cuando se trata de desarrolladores y miembros de los equipos de negocio, lo que permite normalizar el nivel de comprensión del área de problemas y la precisión de la implementación
  • no permite perderse en los montones de documentación y código obsoletos
  • los equipos tienen más confianza en su trabajo y tienden a prever su desarrollo

Conclusión

Para terminar, sólo queríamos añadir que lo más importante es entender por qué y cómo aplicar los diferentes métodos y herramientas de pruebas asistidas por ordenador sin escribir pruebas para nada. La correcta aplicación de los métodos de pruebas asistidas por ordenador y el desarrollo basado en la ejecución de pruebas influyen de forma significativa en el proceso de desarrollo en general y en la calidad del producto final.

uvallie
Maria Filina

Directora de Marketing en Redwerk