2010-08-20 5 views
14

En puestos como this y otros, parece que la forma correcta de hacer TDD es escribir una prueba para una función, obtener solo esa característica para pasar, y luego agregar otra prueba y refactorizar según sea necesario hasta que pase, luego repetir .Al hacer TDD, ¿por qué debería hacer "lo suficiente" para aprobar una prueba?

Mi pregunta es: ¿por qué se utiliza este enfoque? Entiendo completamente la primera idea de las pruebas de escritura, porque ayuda a su diseño. Pero ¿por qué no crearía todas las pruebas para una función específica y luego implementaría esa función de una vez hasta que todas las pruebas pasen?

Respuesta

11

El enfoque proviene del principio de Extreme Programming de You Are not Going to Need It. Si realmente escribe una prueba única y luego el código que la hace pasar y luego repite ese proceso, generalmente encontrará que escribe lo suficiente para que las cosas funcionen. No invente nuevas funciones que no sean necesarias. No manejas casos de esquina que no existen.

Pruebe un experimento. Escriba la lista de exámenes que cree que necesita. Hazlo a un lado. Luego vaya con el enfoque de una prueba a la vez. Vea si las listas son diferentes y por qué. Cuando hago eso, casi siempre termino con menos pruebas. Casi siempre encuentro que inventé un caso que no necesitaba si lo hacía todas las pruebas de primera manera.

+0

+1 para el "experimento" – k3b

1

Esta es una gran pregunta. Debe encontrar un equilibrio entre escribir todas las pruebas en el universo de posibles pruebas y los escenarios de usuario más probables. Una prueba es, en mi humilde opinión, insuficiente, y normalmente me gusta escribir 3 o 4 pruebas que representan los usos más comunes de la función. También me gusta escribir una mejor prueba de caso y una peor prueba de caso también.

Escribir muchas pruebas lo ayuda a anticipar y comprender el posible uso de su función.

+0

¿Puede explicar lo que quiere decir con una prueba de mejor/peor caso? Veo el significado en el mundo real, pero no puedo pensar cómo se aplica a TDD y pruebas unitarias. – Mathias

+0

Tiene razón, la prueba de "mejor/peor caso" no está realmente relacionada con TDD, sino algo que he encontrado que es realmente útil en las pruebas. El mejor caso es el uso para garantizar que no haya regresión después de realizar un cambio para corregir una prueba fallida. – funkymushroom

3

Creo que esto se deriva del principio de "YAGNI" ("No lo necesitarás") (*), que establece que las clases deben ser tan simples como sea necesario, sin características adicionales. Por lo tanto, cuando necesita una característica, escribe una prueba, luego escribe la característica y luego se detiene. Si primero escribió una serie de pruebas, claramente estaría especulando sobre lo que su API debería ser en algún momento en el futuro.

(*) que se traducen generalmente que, como "Eres demasiado estúpido para saber lo que se necesitará en el futuro", pero eso es otro tema ......

0

lo haría como usted sugiere. Escriba varias pruebas para una función específica, implemente la función y asegúrese de que pasen todas las pruebas para esta función. Esto garantiza que comprende el propósito y el uso de la función por separado de su implementación de la misma.

0

Si necesita hacer mucha más implementación que las pruebas de su unidad, entonces las pruebas de su unidad probablemente no sean lo suficientemente exhaustivas.

Creo que parte de esa idea es mantener la simplicidad, mantener las características diseñadas/planificadas y asegurarse de que sus pruebas sean suficientes.

1

creo defensores TDD escribir una prueba a la vez, ya que le obliga a pensar en términos del principio de hacer la cosa más simple que podría funcionar en cada etapa del desarrollo.

3

en la medida en que reduce la posibilidad de sobreescribir la pieza de código que está escribiendo.

Es simplemente más fácil agregar código innecesario cuando se buscan escenarios de uso diferentes.

1

Creo que el artículo que enviaste es exactamente la respuesta. Si primero escribe todas las pruebas y todos los escenarios, probablemente escriba su código para manejar todos esos escenarios al mismo tiempo y la mayoría de las veces probablemente termine con un código que sea bastante complejo para manejar todo esto.

Por otro lado, si va de a uno por vez, terminará refaccionando su código existente cada vez para terminar con el código probablemente tan simple como puede ser para todos los escenarios.

Como en el caso del enlace que brindó en su pregunta, si hubieran escrito todas las pruebas primero, estoy bastante seguro de que no habrían terminado con una simple declaración if/else, pero probablemente una pieza recursiva bastante compleja de código.

2

Dan North ha sugerido que no existe el diseño basado en pruebas porque el diseño no se elimina mediante pruebas, estas pruebas unitarias solo se convierten en pruebas una vez que se implementa la funcionalidad, pero durante la fase de diseño realmente diseñando por ejemplo.

Esto tiene sentido: sus pruebas están configurando un rango de datos de muestra y condiciones con las que el sistema bajo prueba va a operar, y usted expulsa el diseño basado en estos escenarios de ejemplo.

Algunas de las otras respuestas sugieren que esto se basa en YAGNI. Esto es parcialmente cierto.

Sin embargo, más allá de eso, está la cuestión de la complejidad. Como se afirma a menudo, la programación se trata de gestionar la complejidad, dividir las cosas en unidades comprensibles.

Si escribe 10 pruebas para cubrir casos donde param1 es nulo, param2 es nulo, string1 está vacío, int1 es negativo, y el día actual de la semana es un fin de semana, y luego va a implementar eso, está teniendo hacer malabarismos con mucha complejidad a la vez. Esto abre espacio para introducir errores y resulta muy difícil determinar por qué fallan las pruebas.

Por otro lado, si escribe la primera prueba para cubrir una cadena vacía1, apenas tiene que pensar en la implementación. Una vez que la prueba está pasando, pasas a un caso donde el día actual es un fin de semana. Miras el código existente y se hace obvio dónde debe ir la lógica. Ejecutas pruebas y si la primera prueba ahora está fallando, sabes que la rompiste al implementar lo del día de la semana. Incluso te recomendaría que confirmes la fuente entre las pruebas para que, si rompas algo, siempre puedas revertir a un estado pasable e intentes de nuevo.

Hacer poco a poco y luego verificar que funciona reduce drásticamente el espacio para la introducción de defectos, y cuando las pruebas fallan después de la implementación, ha cambiado tan poco el código que es muy fácil identificar el defecto y corrígelo, porque sabes que el código existente ya estaba funcionando correctamente.

4

Para mí, se trata de "carga de pensamiento". Si tengo todos los comportamientos posibles de los que preocuparme a la vez, mi cerebro está tenso. Si los abordo de uno en uno, puedo prestar toda mi atención a la solución del problema inmediato.

1

La razón detrás del principio es simple. Cuán práctico es atenerse es una pregunta separada.

La razón es que si escribe más código que lo que se necesita para pasar la prueba actual, está escribiendo un código que, por definición, no se ha probado. (No tiene nada que ver con YAGNI.)

Si escribe la siguiente prueba para "ponerse al día" con el código de producción, acaba de escribir un error que no ha visto.La prueba se puede llamar "TestNextFeature" pero también puede ser return true para todas las pruebas que tenga.

TDD se trata de asegurarse de que todo el código de producción y pruebas se prueba y de que todos esos errores molestos "pero estoy seguro de que lo escribí bien" no entran en el código.

0

Muchas de las buenas respuestas anteriores - YAGNI es la primera respuesta que me viene a la mente.

La otra cosa importante acerca de la directriz 'acaba de obtener la aprobación de pruebas', sin embargo, es que TDD es en realidad un proceso de tres etapas:

Red> Green> Refactor

volver a visitar con frecuencia la parte final, la refactorización, es donde se entrega mucho del valor de TDD en términos de código más limpio, mejor diseño de API y más confianza en el software. Necesitas refactorizar en bloques realmente pequeños, aunque la tarea sea demasiado grande.

Es difícil acostumbrarse a este hábito, pero quédese con él, ya que es una forma extrañamente satisfactoria de trabajar una vez que ingresa al ciclo.

Cuestiones relacionadas