TDD: ¿Lo estoy haciendo bien?

14

Soy un programador nuevo (solo he estado aprendiendo durante aproximadamente un año) y en mi objetivo de mejorar en eso, recientemente me enteré de TDD. Quería acostumbrarme a usarlo ya que me parece muy útil. Quería verificar y asegurarme de que lo estoy usando correctamente.

Lo que estoy haciendo:

  1. Piensa en un nuevo método que necesito.
  2. Crea una prueba para ese método.
  3. prueba fallida.
  4. método de escritura.
  5. pasar la prueba.
  6. Método de refactor.
  7. Repetir.

Estoy haciendo esto para CADA método que escribo, ¿hay algunos con los que no debería molestarme? Más adelante, por lo general, pienso en una forma de probar mis métodos ya existentes de una manera o situación diferente. ¿Debo hacer estas nuevas pruebas en las que pienso, o dado que cada método ya tiene una prueba propia, no debería molestarme? ¿Puedo SUPERAR la prueba de mi código? Supongo que es mi principal preocupación al preguntar esto.

EDIT

Además, esto era algo que me estaba preguntando. Al hacer algo como hacer una GUI, ¿sería necesario TDD en esa situación? Personalmente, no puedo pensar en cómo escribiría las pruebas para eso.

    
pregunta cgasser 18.05.2012 - 19:52
fuente

9 respuestas

16

Lo que estás describiendo como un flujo de trabajo no es, en mi opinión, el Espíritu de TDD.

La sinopsis del libro de Kent Beck en Amazon dice:

  

Muy simple, el desarrollo guiado por pruebas está destinado a eliminar el miedo en   desarrollo de aplicaciones. Aunque un poco de miedo es saludable (a menudo se lo ve como un   conciencia que le dice a los programadores que "¡cuidado!"), el autor   cree que los subproductos del miedo incluyen tentativo, gruñón y   Programadores no comunicativos que son incapaces de absorber constructivos.   crítica. Cuando los equipos de programación compran en TDD, ven inmediatamente   resultados positivos. Eliminan el miedo involucrado en sus trabajos, y   Están mejor equipados para enfrentar los difíciles desafíos que enfrentan.   TDD elimina los rasgos tentativos, enseña a los programadores a   Comunicarse, y alienta a los miembros del equipo a buscar críticas.   Sin embargo, incluso el autor admite que el mal humor debe ser resuelto   ¡individualmente! En resumen, la premisa detrás de TDD es que el código debe ser   continuamente probado y refactorizado.

TDD práctico

Pruebas automáticas formales, especialmente las Pruebas unitarias, cada método de cada clase es tan malo como un anti-patrón y no prueba nada. Hay un equilibrio que se tendrá. ¿Estás escribiendo pruebas unitarias para cada método setXXX/getXXX , también son métodos?

Las pruebas también pueden ayudar a ahorrar tiempo y dinero, pero no olvide que su desarrollo y su costo son de código y, por lo tanto, cuestan tiempo y dinero de mantenimiento. Si se atrofian por falta de mantenimiento, se convierten en una responsabilidad más que en un beneficio.

Al igual que todo esto, hay un balance que no puede ser definido por nadie más que por ti mismo. Cualquier dogma de cualquier manera es probablemente más incorrecto que correcto.

Una buena métrica es el código que es crítico para la lógica de negocios y está sujeto a modificaciones frecuentes según los requisitos cambiantes. Esas cosas requieren pruebas formales que sean automatizadas, lo que sería un gran retorno de la inversión.

Será muy difícil encontrar muchas tiendas profesionales que funcionen de esta manera. Simplemente no tiene sentido comercial gastar dinero probando cosas que, para todos los propósitos prácticos, nunca cambiarán después de que se realice una simple prueba de humo. Escribir pruebas formales automatizadas de unidades para los métodos .getXXX/.setXXX es un buen ejemplo de esto, una completa pérdida de tiempo.

  

Hace ya dos décadas que se señaló que las pruebas del programa   puede demostrar convincentemente la presencia de errores, pero nunca puede   demostrar su ausencia. Después de citar este comentario tan publicitado.   devotamente, el ingeniero de software vuelve al orden del día y   continúa refinando sus estrategias de prueba, al igual que el alquimista de   Ayer, quien continuó refinando sus purificaciones crisocósmicas.

     

- Edsger W. Djikstra . (Escrito en 1988, por lo que ahora está más cerca de   4.5 décadas.)

Consulte también esta respuesta .

    
respondido por el Jarrod Roberson 18.05.2012 - 20:18
fuente
13

Estás muy cerca. Intenta pensar de esta manera ligeramente diferente.

  1. Piensa en un nuevo comportamiento que necesito.
  2. Crea una prueba para ese comportamiento.
  3. prueba fallida.
  4. Escriba un nuevo método o amplíe el existente.
  5. pasar la prueba.
  6. Código de refactor.
  7. Repetir.

No cree automáticamente getters y setters para cada propiedad . No piense en un método completo y escriba la prueba ( s) para cubrir toda la funcionalidad . Intente encapsular las propiedades dentro de la clase y escriba los métodos para proporcionar el comportamiento que necesita. Deje que sus métodos evolucionen hacia un buen diseño en lugar de intentar planificarlos por adelantado. Recuerde que TDD es un proceso de diseño, no un proceso de prueba. La ventaja que tiene sobre otros procesos de diseño es dejar atrás un flujo de pruebas de regresión automatizadas, en lugar de una hoja de papel que se tira en la papelera.

También, recuerde las tres reglas de TDD del tío Bob .

  1. No se le permite escribir ningún código de producción a menos que sea para hacer una prueba de prueba fallida.
  2. No se le permite escribir más de una prueba de unidad de lo que es suficiente para fallar; y las fallas de compilación son fallas.
  3. No se le permite escribir más código de producción del que sea suficiente para pasar la única prueba de unidad que falla.
respondido por el pdr 18.05.2012 - 20:28
fuente
5

Pocas cosas para agregar a las respuestas de otros:

  1. Hay tal cosa como sobre pruebas. Desea asegurarse de que las pruebas unitarias se superpongan lo menos posible. No tiene sentido que múltiples pruebas verifiquen las mismas condiciones en el mismo código. Por otro lado, cuando refactoriza su código de producción y tiene muchas pruebas que se superponen en esa sección, tendrá que volver y corregir todas esas pruebas. Mientras que si no se superponen, entonces un cambio solo romperá una prueba.

  2. Simplemente porque pensaste en una mejor manera de escribir una prueba, no volvería allí y empezaría a escribirla. Esto se remonta a las personas que siguen escribiendo y reescribiendo la misma clase / función porque tratan de hacerlo perfecto. Nunca será perfecto, así que sigue adelante. Cuando descubra un método mejor, manténgalo en mente (o agregue a los comentarios de la prueba). La próxima vez que esté allí, y verá el beneficio inmediato de cambiar a la nueva forma, ese es el momento de refactorizar. De lo contrario, si la función está terminada y siguió adelante y todo funciona, déjelo funcionando.

  3. TDD se enfoca en brindar un valor inmediato, no simplemente en asegurarse de que cada función sea verificable. Cuando agregue funcionalidad, comience preguntando "qué necesita el cliente". Luego define una interfaz para darle al cliente lo que necesita. Luego implementa lo que sea necesario para hacer que la prueba pase. TDD es casi como probar escenarios de casos de uso (incluidos todos los "qué pasa si"), en lugar de simplemente codificar funciones públicas y probar cada una.

  4. Usted preguntó acerca de probar el código GUI. Busque los patrones "Humble Dialog" y "MVVM". La idea detrás de estos dos es que creas un conjunto de clases de "modelo de vista", que en realidad no tienen lógica específica de UI. Sin embargo, estas clases tendrán toda la lógica empresarial que normalmente forma parte de su interfaz de usuario y estas clases deberían ser 100% comprobables. Lo que queda es un shell de IU muy delgado y sí, normalmente ese shell se queda sin cobertura de prueba, pero en ese momento no debería tener casi ninguna lógica.

  5. Si tiene una gran parte del código existente, como pocos otros sugirieron, no debe comenzar a agregar pruebas unitarias en todas partes. Le llevará una eternidad y no obtendrá beneficios al agregar pruebas de unidad al 80% de las clases que son estables y no cambiarán en el futuro cercano (o no tan cerca). Sin embargo, para nuevos trabajos, encuentro que el desarrollo de TDD con TODO el código es extremadamente beneficioso. No solo terminas con una suite con pruebas automatizadas cuando terminas, sino que el desarrollo real tiene enormes beneficios:

    • Al considerar la capacidad de prueba, escribirá un código que sea menos acoplado y más modular
    • Al considerar su contrato público antes que cualquier otra cosa, terminará con interfaces públicas que son mucho más limpias
    • Mientras escribe el código, la verificación de la nueva funcionalidad lleva milisegundos en comparación con la ejecución de toda la aplicación e intenta forzar la ejecución por el camino correcto. Mi equipo aún libera un código de manejo de errores que ni siquiera se ejecutó UNA VEZ simplemente porque no lograron que se cumplieran las condiciones adecuadas. Es sorprendente cuánto tiempo desperdiciamos cuando, más adelante, en el control de calidad, esas condiciones ocurren. Y sí, gran parte de este código es lo que alguien consideraría "no es un área para muchos cambios en el futuro una vez que se realicen las pruebas de humo".
respondido por el DXM 18.05.2012 - 22:43
fuente
1

Hay algunos métodos que no se están probando, a saber, esas pruebas. Sin embargo, hay algo que decir para algunas pruebas que se agregan después de que se haya escrito el código inicial, como las condiciones de contorno y otros valores, de modo que pueda haber múltiples pruebas en un solo método.

Si bien puede hacer una prueba excesiva de su código, eso suele ocurrir cuando alguien quiere probar todas las permutaciones posibles de las entradas, lo que no suena como lo que está haciendo. Por ejemplo, si tiene un método que toma un carácter, ¿escribe una prueba para cada valor posible que se pueda ingresar? Ahí sería donde obtendría pruebas excesivas, OMI.

    
respondido por el JB King 18.05.2012 - 19:59
fuente
1

Generalmente lo estás haciendo bien.

Las pruebas son código. Así que si puedes mejorar la prueba, ve y refactorízala. Si crees que se puede mejorar una prueba, adelante, cámbiala. No tenga miedo de reemplazar una prueba con una mejor.

Recomiendo al probar su código, evite especificar cómo se supone que el código hace lo que está haciendo. Las pruebas deben mirar los resultados de los métodos. Esto ayudará con la refactorización. Algunos métodos no necesitan ser probados explícitamente (es decir, simples de obtención y configuración) porque los usarás para verificar los resultados de otras pruebas.

    
respondido por el Schleis 18.05.2012 - 20:06
fuente
0

Mi opinión sobre TDD es que las herramientas han creado un mundo de desarrolladores de estilo 'apuntar y hacer clic'. El hecho de que las herramientas creen un talón de prueba para cada método no significa que deba escribir pruebas para cada método. Algunas personas están 'cambiando el nombre' de TDD como BDD (desarrollo impulsado por el comportamiento) donde las pruebas son mucho más amplias y están destinadas a probar el comportamiento de la clase, no cada método extremadamente pequeño.

Si diseñas tus pruebas para probar la clase como se pretende utilizar, entonces comienzas a obtener algunos beneficios, especialmente cuando comienzas a escribir pruebas que se ejercitan un poco más que cada método, especialmente cuando comienzas a probar las La interacción de esos métodos. Supongo que podrías pensar en ello como escribir pruebas para una clase, en lugar de métodos. En cualquier caso, aún debe escribir 'pruebas de aceptación' que ejerzan la combinación de métodos para asegurarse de que no haya contradicciones o conflictos en la forma en que se usan juntos.

No se confunda TDD con las pruebas, no lo es. TDD está diseñado para que usted escriba código para ejercer sus requisitos, no para probar los métodos. Es un punto sutil pero importante que a menudo se pierde en las personas que escriben ciegamente el código de prueba para cada método. Es que deberías estar escribiendo pruebas que aseguren que tu código haga lo que quieres que haga, no que el código que escribiste funcione como se supone.

Hay algunos buenos enlaces a la derecha sobre BDD v TDD. Échales un vistazo.

    
respondido por el gbjbaanb 18.05.2012 - 20:16
fuente
0

Cuando comiences aprendiendo TDD, sí, deberías seguir ciegamente el enfoque dogmático de no escribir una sola línea de código, excepto para hacer una prueba fallida, y escribir solo la prueba suficiente para fallar (y falla por la razón correcta / esperada).

Una vez que haya aprendido de qué se trata el TDD, ENTONCES puede decidir que no vale la pena probar ciertos tipos de cosas. Este es el mismo enfoque que debe seguir para todo, y las artes marciales japonesas lo llaman " shuhari ". (El enlace también explica cómo se puede avanzar a través de las etapas de aprendizaje sin un maestro, que es, sospecho, cómo la mayoría de la gente tiene que aprender).

    
respondido por el Frank Shearar 22.05.2012 - 12:02
fuente
0

Creo que estás exagerando.

He estado practicando TDD durante muchos años y, según mi experiencia, cuando TDD se realiza de manera efectiva, obtiene dos beneficios principales:

  • Proporcionar comentarios rápidos
  • Habilitar refactorización

Proporcionar comentarios rápidos

Particularmente con lenguajes dinámicos, puedo ejecutar las pruebas relevantes en menos de un segundo. Y tengo observadores del sistema de archivos que ejecutan estas pruebas automáticamente cuando se cambia un archivo de origen en el disco. Por lo tanto, prácticamente no tengo tiempo de espera para las pruebas, y de inmediato sé si el código que escribí funcionó como se esperaba. Así, TDD conduce a una forma muy eficiente de trabajar.

Habilitar refactorización

Si tiene un buen conjunto de pruebas, puede refactorizarlo de manera segura, a medida que obtiene nuevos conocimientos sobre cómo debe diseñarse el sistema.

Un buen conjunto de pruebas le permite trasladar la responsabilidad en su código y tener la confianza de que el código funciona como se esperaba después de la mudanza. Y debería poder hacer esto con pequeños cambios en el código de prueba.

Si escribe pruebas para cada método en su sistema, es probable que no pueda refactorizar fácilmente su código, cada refactor de su código requerirá cambios masivos en el código de prueba. ¿Y puede estar seguro de que el código de prueba todavía funciona como se esperaba? ¿O introdujo accidentalmente un error en el código de prueba, que en consecuencia conduce a un error en el código de producción?

Si, sin embargo, como también se sugiere en respuesta de pdr , concéntrese en el comportamiento En lugar de métodos al escribir pruebas, tendrá pruebas que requerirán muchos menos cambios al refactorizar el sistema.

O como dice Ian Cooper en esta presentación (cité de memoria, por lo que podría no estar correctamente citado):

  

Tu razón para escribir una nueva prueba debería ser agregar un nuevo comportamiento,   no agregar una nueva clase

    
respondido por el Pete 25.07.2017 - 08:39
fuente
-2

Debes probar todos los métodos públicos .

El problema aquí es que si sus métodos públicos son muy pequeños, probablemente esté exponiendo demasiada información. La práctica común de exponer cada propiedad como getXXX() realmente rompe la encapsulación.

Si sus métodos públicos son en realidad el comportamiento de la clase, entonces debería probarlos. Si no, no son buenos métodos públicos.

EDITAR: la respuesta de pdr es mucho más completa que la mía.

    
respondido por el Chip 18.05.2012 - 22:20
fuente

Lea otras preguntas en las etiquetas