¿Se puede aplicar la metodología TDD de arriba a abajo?

13

No estoy claro cómo TDD, la metodología, maneja el siguiente caso. Supongamos que quiero implementar el algoritmo mergesort, en Python. Empiezo escribiendo

assert mergesort([]) === []

y la prueba falla con

NameError: el nombre 'mergesort' no está definido

Luego agrego

def mergesort(a):
    return []

y mi prueba pasa A continuación agrego

assert mergesort[5] == 5

y mi prueba falla con

AserciónError

que hago pasar con

def mergesort(a):
    if not a:
        return []
    else:
        return a

A continuación, agrego

assert mergesort([10, 30, 20]) == [10, 20, 30]

y ahora tengo que intentar hacer que esto pase. "Conozco" el algoritmo mergesort, así que escribo:

def mergesort(a):
    if not a:
        return []
    else:
        left, right = a[:len(a)//2], a[len(a)//2:]
        return merge(mergesort(left)), mergesort(right))

Y esto falla con

NameError: el nombre 'fusionar' no está definido

Ahora aquí está la pregunta. ¿Cómo puedo salir corriendo y comenzar a implementar mergeusando TDD? Parece que no puedo porque tengo esta prueba de "suspensión" sin cumplir mergesort, que no se cumplemerge , ¡ que no pasará hasta que termine! Si esta prueba se detiene, realmente nunca puedo hacer TDD porque no seré "verde" durante la construcción de las iteraciones de TDD merge.

Parece que estoy atrapado en los siguientes tres escenarios feos, y me gustaría saber (1) cuál de estos prefiere la comunidad TDD, o (2) ¿hay otro enfoque que me estoy perdiendo? ¡He visto varios recorridos del Tío Bob TDD y no recuerdo haber visto un caso como este antes!

Aquí están los 3 casos:

  1. Implemente la fusión en un directorio diferente con un conjunto de pruebas diferente.
  2. No se preocupe por ser ecológico cuando desarrolle la función auxiliar, solo realice un seguimiento manual de las pruebas que realmente desea aprobar.
  3. Comente (GASP!) O elimine las líneas en mergesortesa llamada merge; luego, después de llegar mergeal trabajo, vuelva a colocarlos.

Todo esto me parece tonto (¿o estoy mirando esto mal?). ¿Alguien sabe el enfoque preferido?

Ray Toal
fuente
2
Parte del objetivo de TDD es ayudarlo a crear un diseño de software. Parte de ese proceso de diseño es descubrir lo que se necesita para producir el resultado deseado. En el caso de mergesort, dado que ya es un algoritmo muy bien definido, este proceso de descubrimiento no es necesario, y luego se convierte en una cuestión de mapear lo que ya sabe que es el diseño para una serie de pruebas unitarias. Presumiblemente, su prueba de nivel superior afirma que su método bajo prueba acepta una colección sin clasificar y devuelve una ordenada ...
Robert Harvey
1
... Las pruebas unitarias posteriores gradualmente profundizarían en la mecánica real de a mergesort. Si está buscando la forma "correcta" de hacer esto, no hay otra, que no sea ser exacto acerca de su mapeo del mergesortalgoritmo a una serie de pruebas unitarias; es decir, deberían reflejar lo que mergesortrealmente hace.
Robert Harvey
44
El diseño no crece solo a partir de pruebas unitarias; Si espera que un mergesortdiseño emerja naturalmente del refactor rojo-verde, eso no sucederá a menos que guíe el proceso en función de su conocimiento actual mergesort.
Robert Harvey, el
1
En TDD mergedebe inventarse solo en la etapa de "refactorización". Si ve que mergese puede introducir ese método para aprobar la prueba mergesort, primero haga que sus pruebas pasen sin mergemétodo. Luego refactorice su implementación introduciendo el mergemétodo.
Fabio

Respuestas:

13

Aquí hay algunas formas alternativas de ver sus opciones. Pero primero, las reglas de TDD, del tío Bob con énfasis en mí:

  1. No está permitido escribir ningún código de producción a menos que sea para aprobar una prueba de unidad que falla.
  2. No se le permite escribir más de una prueba unitaria de la que es suficiente para fallar; y las fallas de compilación son fallas.
  3. No está permitido escribir más código de producción del que sea suficiente para pasar la prueba de una unidad que falla.

Entonces, una forma de leer la regla número 3 es que necesita la mergefunción para pasar la prueba, de modo que pueda implementarla, pero solo en su forma más básica.

O, alternativamente, comienza escribiendo la operación de fusión en línea, y luego la refactoriza en una función después de hacer que la prueba funcione.

Otra interpretación es que está escribiendo mergesort, sabe que necesitará una mergeoperación (es decir, no es YAGNI, que es lo que la regla "suficiente" intenta reducir). Por lo tanto, debería haber comenzado con las pruebas para la fusión, y solo luego continuar con las pruebas para la clasificación general.

kdgregory
fuente
Estas son realmente buenas observaciones. Había pensado en el factoring en línea y antes, pero como mergees sorprendentemente desordenado, en cuanto al caso (así como útil como un autónomo), la idea de hacerlo como una función separada tenía más sentido. Sin embargo, el estilo de hacerlo en línea en su forma básica y luego factorizarlo en la etapa de sombrero azul realmente parece ser correcto y mucho lo que estaba buscando.
Ray Toal
@RayToal: en realidad, me inclino por el enfoque de probar completamente la mergeoperación antes de hacer la clasificación (así como también realizar pruebas por separado de la partitionoperación). Creo que el diseño emergente de los beneficios reclamados proviene de trabajar lentamente hacia un objetivo conocido. En el caso de mergesort, no creo que el objetivo sea la clasificación en general (porque entonces terminarás con una clasificación de burbujas). Conoces las operaciones básicas, así que trabajas para esas operaciones; el tipo es principalmente una idea de último momento.
kdgregory
1
Hay una cuarta opción. Pase la mergefunción mergesorty simule su comportamiento. Luego regrese e implemente la mergeprueba primero. Los delegados son increíbles ™.
RubberDuck
@RubberDuck Burlarse de una parte integral del dominio central podría generar algunos problemas, más específicamente cuando ejecuta el programa y la función de burla y fusión difiere en el menor detalle. Se debe dejar un enfoque como ese para las instancias en las que está trabajando con recursos externos, como de dónde proviene la lista para ordenar.
cllamach