JavaRush /Blog Java /Random-ES /¿Qué es TDD y las pruebas unitarias? [traducción]
Dr-John Zoidberg
Nivel 41
Марс

¿Qué es TDD y las pruebas unitarias? [traducción]

Publicado en el grupo Random-ES
Este artículo es una adaptación de un capítulo del libro The Complete Software Career Guide. Su autor, John Sonmez, lo escribe y publica algunos capítulos en su sitio web.
¿Qué es TDD y las pruebas unitarias [traducción] - 1

Un breve glosario para principiantes.

Las pruebas unitarias o pruebas unitarias son un proceso de programación que le permite verificar la corrección de módulos individuales del código fuente de un programa. La idea es escribir pruebas para cada función o método no trivial. Prueba de regresión es un nombre general para todo tipo de pruebas de software destinadas a detectar errores en áreas del código fuente ya probadas. Estos errores, cuando, después de realizar cambios en el programa, algo que debería seguir funcionando deja de funcionar, se denominan errores de regresión. Resultado rojo, falla : falla de la prueba. La diferencia entre el resultado esperado y el real. Resultado verde, aprobado : resultado positivo de la prueba. El resultado real no es diferente del obtenido. ***
¿Qué es TDD y las pruebas unitarias [traducción] - 2
Tengo una relación muy mixta con Test Driven Development (TDD) y las pruebas unitarias, yendo del amor al odio y viceversa. Yo era un ávido admirador y al mismo tiempo un escéptico sospechoso sobre el uso de esta y otras "mejores prácticas". La razón de mi actitud se basa en el hecho de que ha surgido un problema grave en los procesos de desarrollo de software: los desarrolladores, y en ocasiones los gerentes, utilizan ciertas herramientas y metodologías solo porque pertenecen a las “mejores prácticas”. La verdadera razón de su uso aún no está clara. Un día comencé a trabajar en un proyecto determinado y en el proceso me informaron que modificaríamos el código cubierto por una gran cantidad de pruebas unitarias. No es broma, había alrededor de 3000. Esto suele ser una buena señal, una señal de que los desarrolladores están utilizando metodologías avanzadas. El código con este enfoque suele estar estructurado y se basa en una arquitectura bien pensada. En una palabra, la presencia de pruebas me hizo feliz, aunque sólo fuera porque significaba facilitar mi trabajo como mentor de programadores. Como ya teníamos pruebas unitarias, todo lo que tenía que hacer era conectar al equipo de desarrollo para brindarles soporte y comenzar a escribir nuestro propio código. Abrí el IDE (entorno de desarrollo integrado) y cargué el proyecto.
¿Qué es TDD y las pruebas unitarias [traducción] - 3
¡Fue un gran proyecto! Encontré una carpeta con la etiqueta "pruebas unitarias". “Genial”, pensé. - Lancemoslo y veamos que pasa. Solo tomó unos minutos y, para mi sorpresa, todas las pruebas pasaron, todo estaba en verde ( "verde" es un resultado positivo de la prueba. Indica que el código está funcionando como se esperaba. El rojo indica "falla" o falla, entonces Hay un caso en el que el código no funciona correctamente (nota del traductor ). Todos pasaron la prueba. En ese momento, el escéptico que había en mí despertó. ¿Cómo es posible que tres mil pruebas unitarias las hicieran todas a la vez y dieran un resultado positivo? En mi larga práctica, no podía recordar un momento en el que comencé a trabajar en un proyecto sin una sola prueba unitaria negativa en el código. ¿Qué hacer? ¡Compruébalo manualmente! ChY eligió una prueba al azar, no la más reveladora, pero inmediatamente quedó claro lo que estaba comprobando. Pero mientras lo hacía, noté algo absurdo: ¡la prueba no contenía ninguna comparación con el resultado esperado (afirma)! Es decir, ¡en realidad no se comprobó nada ! Hubo ciertos pasos en la prueba, se llevaron a cabo, pero al final de la prueba, donde debía comparar los resultados reales y esperados, no hubo ninguna verificación. La "prueba" no probó nada. Abrí otra prueba. Aún mejor: el operador de comparación con el resultado ha sido comentado. ¡Brillantemente! Esta es una excelente manera de realizar una prueba, simplemente comente el código que está causando que falle. Revisé otra prueba, luego otra... Ninguno comprobó nada. Tres mil pruebas y todas ellas son completamente inútiles. Existe una gran diferencia entre escribir pruebas unitarias y comprender las pruebas unitarias y el desarrollo basado en pruebas (TDD).

¿Qué son las pruebas unitarias?

¿Qué es TDD y las pruebas unitarias [traducción] - 4
La idea básica de las pruebas unitarias es escribir pruebas que prueben la "unidad" más pequeña de código. Las pruebas unitarias suelen estar escritas en el mismo lenguaje de programación que el código fuente de la aplicación. Se crean directamente para probar este código. Es decir, las pruebas unitarias son códigos que verifican la exactitud de otro código. Utilizo la palabra "prueba" con bastante libertad en el contexto, porque las pruebas unitarias en cierto sentido no son pruebas. No experimentan nada. Lo que quiero decir es que cuando ejecutas una prueba unitaria normalmente no descubres que algún código no funciona. Esto lo descubres mientras escribes la prueba, porque cambiarás el código hasta que la prueba se vuelva verde. Sí, el código puede cambiar más adelante y entonces la prueba puede fallar. En este sentido, una prueba unitaria es una prueba de regresión. Una prueba unitaria no es como una prueba normal en la que tienes que seguir algunos pasos y ver si el software funciona correctamente o no. Durante el proceso de escritura de una prueba unitaria, descubre si el código hace lo que se supone que debe hacer o no, y cambiará el código hasta que pase la prueba.
¿Qué es TDD y las pruebas unitarias [traducción] - 5
¿Por qué no escribir una prueba unitaria y comprobar si se aprueba? Si lo piensa de esta manera, las pruebas unitarias se convierten en una especie de requisitos absolutos para ciertos módulos de código en un nivel muy bajo. Puede pensar en una prueba unitaria como una especificación absoluta . Una prueba unitaria determina que bajo estas condiciones, con este conjunto particular de entradas, hay una salida que debes obtener de esta unidad de código. Las verdaderas pruebas unitarias identifican la unidad de código coherente más pequeña, que en la mayoría de los lenguajes de programación, al menos en los orientados a objetos, es una clase.

¿Qué a veces se llama prueba unitaria?

¿Qué es TDD y las pruebas unitarias [traducción] - 6
Las pruebas unitarias a menudo se confunden con las pruebas de integración. Algunas "pruebas unitarias" prueban más de una clase o prueban grandes unidades de código. Muchos desarrolladores afirman que escriben pruebas unitarias cuando en realidad escriben pruebas de caja blanca de bajo nivel. No discutas con estos tipos. Solo sepa que en realidad escriben pruebas de integración, y las verdaderas pruebas unitarias prueban la unidad más pequeña de código aislada de otras partes. Otra cosa que a menudo se denomina prueba unitaria son las pruebas unitarias sin comparar con un valor esperado. En otras palabras, pruebas unitarias que en realidad no prueban nada. Cualquier prueba, unificada o no, debe incluir algún tipo de verificación; lo llamamos comparar el resultado real con el resultado esperado. Es esta conciliación la que determina si la prueba pasa o falla. Una prueba que siempre pasa es inútil. Una prueba que siempre falla es inútil.

El valor de las pruebas unitarias

¿Por qué soy un entusiasta de las pruebas unitarias? ¿Por qué es perjudicial llamar pruebas generalizadas, que implican probar no el bloque más pequeño aislado de otro código, sino un fragmento de código más grande, “prueba unitaria”? ¿Cuál es el problema si algunas de mis pruebas no comparan los resultados recibidos y esperados? Al menos ejecutan el código. Intentaré explicarlo.
¿Qué es TDD y las pruebas unitarias [traducción] - 7
Hay dos razones principales para realizar pruebas unitarias. El primero es mejorar el diseño del código. ¿Recuerdas que dije que las pruebas unitarias no son realmente pruebas? Cuando escribe pruebas unitarias adecuadas, se obliga a aislar la unidad de código más pequeña. Estos intentos le llevarán a descubrir problemas en la estructura del propio código. Puede que le resulte muy difícil aislar la clase de prueba y no incluir sus dependencias, y esto puede hacerle darse cuenta de que su código está demasiado estrechamente acoplado. Es posible que descubra que la funcionalidad principal que está intentando probar abarca varios módulos, lo que le llevará a creer que su código no es lo suficientemente coherente. Cuando te sientas a escribir una prueba unitaria, es posible que de repente descubras (¡y créeme, sucede!) que no tienes idea de lo que se supone que debe hacer el código. En consecuencia, no podrá escribir una prueba unitaria para ello. Y, por supuesto, puede encontrar un error real en la implementación del código, ya que la prueba unitaria lo obliga a pensar de manera innovadora y probar diferentes conjuntos de entradas que quizás no haya considerado.
¿Qué es TDD y las pruebas unitarias [traducción] - 8
Si cumple estrictamente con la regla de "probar la unidad más pequeña de código aislada de las demás" al crear pruebas unitarias, seguramente encontrará todo tipo de problemas con ese código y el diseño de esos módulos. En el ciclo de vida del desarrollo de software, las pruebas unitarias son más una actividad de evaluación que una actividad de prueba. El segundo objetivo principal de las pruebas unitarias es crear un conjunto automatizado de pruebas de regresión que puedan actuar como una especificación de bajo nivel del comportamiento del software. ¿Qué significa? Cuando amasas la masa, no la rompes. Desde este punto de vista, las pruebas unitarias son pruebas, más concretamente pruebas de regresión. Sin embargo, el propósito de las pruebas unitarias no es simplemente crear pruebas de regresión. En la práctica, las pruebas unitarias rara vez detectan regresiones, ya que un cambio en la unidad de código que se está probando casi siempre contiene cambios en la prueba unitaria misma. Las pruebas de regresión son mucho más efectivas en un nivel superior, cuando el código se prueba como una "caja negra", porque en este nivel la estructura interna del código se puede cambiar, mientras que se espera que el comportamiento externo siga siendo el mismo. Las pruebas unitarias, a su vez, verifican la estructura interna para que cuando esa estructura cambie, las pruebas unitarias no fallen. Se vuelven inutilizables y ahora es necesario cambiarlos, desecharlos o reescribirlos. Ahora sabe más sobre el verdadero propósito de las pruebas unitarias que muchos desarrolladores de software veteranos.

¿Qué es el desarrollo basado en pruebas (TDD)?

¿Qué es TDD y las pruebas unitarias [traducción] - 9
En el proceso de desarrollo de software, una buena especificación vale su peso en oro. El enfoque TDD es que antes de escribir cualquier código, primero se escribe una prueba que servirá como especificación, es decir, definir qué debe hacer el código. Este es un concepto de desarrollo de software extremadamente poderoso, pero a menudo se utiliza incorrectamente. Normalmente, el desarrollo basado en pruebas significa utilizar pruebas unitarias para guiar la creación del código de la aplicación. Pero, de hecho, este enfoque se puede aplicar a cualquier nivel. Sin embargo, en este artículo asumiremos que estamos utilizando pruebas unitarias para nuestra aplicación. El enfoque TDD pone todo patas arriba y, en lugar de escribir código primero y luego escribir pruebas unitarias para probar ese código, primero escribe una prueba unitaria y luego escribe código para que esa prueba sea verde. De esta manera, las pruebas unitarias “impulsan” el desarrollo de código. Este proceso se repite una y otra vez. Escribe otra prueba que define más funcionalidades de lo que debe hacer el código. Luego escribe y modifica el código para garantizar que la prueba se complete correctamente. Una vez que tengas un resultado verde, comienzas a refactorizar el código, es decir, refactorizarlo o limpiarlo para hacerlo más conciso. Esta cadena de procesos se suele llamar "Refactorización Roja-Verde" porque primero la prueba unitaria falla (rojo), luego se escribe el código para adaptarse a la prueba, asegurándose de que tenga éxito (verde), y finalmente se optimiza el código ( refactorización). .

¿Cuál es el objetivo de TDD?

¿Qué es TDD y las pruebas unitarias [traducción] - 10
El desarrollo basado en pruebas (TDD), al igual que las pruebas unitarias, se puede utilizar incorrectamente. Es muy fácil llamar "TDD" a lo que haces e incluso seguir la práctica sin entender por qué lo haces de esa manera. El mayor valor de TDD es que se realizan pruebas para producir especificaciones de calidad. TDD es esencialmente la práctica de escribir especificaciones precisas que se pueden verificar automáticamente antes de escribir la codificación. Las pruebas son las mejores especificaciones porque no mienten. No te lo dirán después de dos semanas de tormento con el código "eso no es lo que quise decir en absoluto". Las pruebas, cuando se escriben correctamente, se aprueban o no. Las pruebas indican claramente lo que debería suceder en determinadas circunstancias. Por lo tanto, el objetivo de TDD es brindarnos una comprensión completa de lo que necesitamos implementar antes de comenzar a implementarlo. Si está comenzando con TDD y no puede entender qué se supone que debe evaluar la prueba, entonces necesita hacer más preguntas. Otra función importante de TDD es preservar y optimizar el código. El mantenimiento del código es caro. A menudo bromeo diciendo que el mejor programador es el que escribe el código más corto que resuelve algún problema. O incluso el que demuestra que este problema no necesita solución, y por lo tanto elimina completamente el código, ya que fue este programador quien encontró la manera correcta de reducir la cantidad de errores y reducir el costo de mantenimiento de la aplicación. Al usar TDD, puede estar absolutamente seguro de que no está escribiendo ningún código innecesario, ya que solo escribirá código para pasar pruebas. Existe un principio de desarrollo de software llamado YAGNI (no lo necesitarás). TDD previene YAGNI.

Flujo de trabajo típico de desarrollo basado en pruebas (TDD)

¿Qué es TDD y las pruebas unitarias [traducción] - 11
Comprender el significado de TDD desde un punto de vista puramente académico es difícil. Entonces, veamos un ejemplo de una sesión TDD. Imagínese sentarse en su escritorio y esbozar rápidamente lo que cree que será un diseño de alto nivel para una función que permite a un usuario iniciar sesión en una aplicación y cambiar su contraseña si la olvida. Decide que comenzará con la primera implementación de la función de inicio de sesión, creando una clase que manejará toda la lógica del proceso de inicio de sesión. Abres tu editor favorito y creas una prueba unitaria llamada "El inicio de sesión vacío impide que el usuario inicie sesión". Escribe un código de prueba unitaria que primero crea una instancia de la clase Login (que aún no ha creado). Luego escribe código para llamar a un método en la clase Login que pasa un nombre de usuario y contraseña vacíos. Finalmente, escribe una verificación con el resultado esperado, verificando que el usuario con un inicio de sesión vacío en realidad no haya iniciado sesión. Estás intentando ejecutar una prueba, pero ni siquiera se compila porque no tienes una clase de inicio de sesión. Usted soluciona esta situación y crea una clase de inicio de sesión junto con un método en esa clase para iniciar sesión y otro para verificar el estado del usuario para ver si ha iniciado sesión. Hasta ahora no has implementado la funcionalidad de esta clase y el método que necesitamos. Ejecute la prueba en este punto. Ahora se compila, pero falla inmediatamente.
¿Qué es TDD y las pruebas unitarias [traducción] - 12
Ahora regresa al código e implementa la funcionalidad para pasar la prueba. En nuestro caso, esto significa que deberíamos obtener el resultado: "el usuario no ha iniciado sesión". Ejecutas la prueba nuevamente y ahora pasa. Pasemos a la siguiente prueba. Ahora imaginemos que necesita escribir una prueba llamada "El usuario inicia sesión si ingresó un nombre de usuario y contraseña válidos". Escribe una prueba unitaria que crea una instancia de la clase Login e intenta iniciar sesión con un nombre de usuario y contraseña. En una prueba unitaria, escribe una declaración en la que la clase Login debe responder sí a la pregunta de si el usuario ha iniciado sesión. Ejecuta esta nueva prueba y, por supuesto, falla porque su clase de inicio de sesión siempre devuelve que el usuario no ha iniciado sesión. Regresa a su clase de inicio de sesión e implementa algún código para verificar que el usuario haya iniciado sesión. En este caso, tendrás que descubrir cómo aislar este módulo. Por ahora, la forma más sencilla de hacerlo es codificar el nombre de usuario y la contraseña que utilizó en la prueba y, si coinciden, devolver el resultado "el usuario ha iniciado sesión". Realiza este cambio, ejecuta ambas pruebas y ambas pasan. Pasemos al último paso: observa el código generado y busca una manera de reorganizarlo y simplificarlo. Entonces el algoritmo TDD es:
  1. Creó una prueba.
  2. Escribimos código para esta prueba.
  3. Refactorizado el código.

conclusiones

¿Qué es TDD y las pruebas unitarias [traducción] - 13
Eso es todo lo que quería contarles sobre las pruebas unitarias y TDD en esta etapa. De hecho, existen muchas dificultades asociadas al intento de aislar módulos de código, ya que el código puede ser muy complejo y confuso. Muy pocas clases existen en completo aislamiento. En cambio, tienen dependencias, y esas dependencias tienen dependencias, y así sucesivamente. Para hacer frente a este tipo de situaciones, el veterano de TDD utiliza simulacros, que ayudan a aislar clases individuales reemplazando objetos en módulos dependientes. Este artículo es solo una descripción general y una introducción algo simplificada a las pruebas unitarias y TDD; no entraremos en detalles sobre módulos ficticios y otras técnicas de TDD. La idea es brindarle los conceptos y principios básicos de TDD y las pruebas unitarias que, con suerte, ya tiene. Original: https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION