No entiendo cómo TDD me ayuda a obtener un buen diseño si necesito un diseño para comenzar a probarlo

49

Estoy tratando de envolver mi cabeza alrededor de TDD, específicamente la parte de desarrollo. He visto algunos libros, pero los que encontré abordan principalmente la parte de prueba: la Historia de NUnit, por qué la prueba es buena, Red / Green / Refactor y cómo crear una calculadora de cadenas.

Cosas buenas, pero eso es "solo" Pruebas unitarias, no TDD. Específicamente, no entiendo cómo TDD me ayuda a obtener un buen diseño si necesito un Diseño para comenzar a probarlo.

Para ilustrar, imagine estos 3 requisitos:

  • Un catálogo debe tener una lista de productos
  • El catálogo debe recordar qué productos vio un usuario
  • Los usuarios deben poder buscar un producto

En este punto, muchos libros sacan un conejo mágico de un sombrero y simplemente se sumergen en "Probando el Servicio de Producto", pero no explican cómo llegaron a la conclusión de que hay un Servicio de Producto en primer lugar. Esa es la parte de "Desarrollo" en TDD que estoy tratando de entender.

Es necesario que exista un diseño, pero no se puede encontrar nada fuera de los servicios de la entidad (es decir, hay un Producto, por lo que debería haber un Servicio de Producto) (por ejemplo, el segundo requisito requiere que tenga algunos El concepto de un usuario, pero ¿dónde pondría la funcionalidad para recordarlo? ¿Y es Search una función del ProductService o un SearchService separado? ¿Cómo sabría cuál debería elegir?)

Según SOLID , necesitaría un UserService, pero si diseño un sistema sin TDD, podría terminar con un montón de servicios de método único. ¿TDD no tiene la intención de hacerme descubrir mi diseño en primer lugar?

Soy un desarrollador de .net, pero los recursos de Java también funcionan. Creo que no parece haber una aplicación o libro de muestra real que se ocupe de una línea real de aplicación comercial. ¿Puede alguien proporcionar un ejemplo claro que ilustre el proceso de creación de un diseño utilizando TDD?

    
pregunta Michael Stum 29.05.2013 - 19:29

11 respuestas

17

La idea de TDD es comenzar con las pruebas y trabajar a partir de eso. Por lo tanto, tomar el ejemplo de "Un catálogo necesita tener una lista de productos" podría considerarse como una prueba de "Verificar productos en catálogo" y, por lo tanto, esta es la primera prueba. Ahora, ¿qué contiene un catálogo? ¿Qué contiene un producto? Esas son las siguientes piezas y la idea es juntar algunas piezas y piezas que sería algo así como un Servicio de Producto que nacerá a partir de la primera prueba que se realizará.

La idea de TDD es comenzar con una prueba y luego escribir el código que hace que esa prueba pase como primer punto. Las pruebas de unidad son parte de esto, sí, pero no está viendo la imagen general que se forma al comenzar con las pruebas y luego escribir el código para que no haya puntos ciegos en este punto ya que todavía no hay ningún código.

Tutorial de desarrollo guiado por pruebas donde las diapositivas 20-22 son las claves. La idea es saber qué debe hacer la funcionalidad como resultado, escribir una prueba para ello y luego construir una solución. La parte de diseño variará según lo que se requiera, puede o no ser tan simple de hacer. Un punto clave es utilizar TDD desde el principio en lugar de tratar de introducir tarde en un proyecto. Si comienza con las pruebas primero, esto puede ayudar y es probable que valga la pena señalarlo en un sentido. Si intenta agregar las pruebas más tarde, se convierte en algo que puede postergarse o retrasarse. Las diapositivas posteriores también pueden ser útiles también.

Una de las principales ventajas de TDD es que al comenzar con las pruebas, inicialmente no está sujeto a un diseño. Por lo tanto, la idea es construir las pruebas y crear el código que pasará esas pruebas como una metodología de desarrollo. Un Big Design Up Front puede causar problemas, ya que esto da la idea de bloquear las cosas en su lugar, lo que hace que el sistema se construya para ser menos ágil al final.

Robert Harvey agregó esto en los comentarios que vale la pena indicar en la respuesta:

  

Desafortunadamente, creo que este es un error común acerca de TDD:    no puede hacer crecer una arquitectura de software simplemente escribiendo pruebas unitarias y haciéndolas pasar. La escritura de pruebas unitarias influye en el diseño, pero   no crea el diseño. Tienes que hacer eso.

    
respondido por el JB King 29.05.2013 - 19:43
8

Para lo que vale, TDD me ayuda a encontrar el mejor diseño más rápido que no hacer TDD. Probablemente vendría al mejor diseño con o sin él. Pero ese tiempo en el que me hubiera pasado pensándolo bien y tomando algunas puñaladas en el código se dedica a escribir pruebas en su lugar. Y es menos tiempo. Para mi. No para todos. Y, aunque tomara la misma cantidad de tiempo, me dejaría con un conjunto de pruebas, por lo que la refactorización sería más segura, lo que llevaría a un código aún mejor en la línea.

¿Cómo lo hace?

Primero, me anima a pensar en cada clase como un servicio a algún código de cliente. Un código mejor proviene de pensar en cómo el código de llamada desea usar la API en lugar de preocuparse por el aspecto del código en sí.

En segundo lugar, me impide escribir demasiada complejidad ciclométrica en un método, mientras lo estoy pensando. Cada ruta adicional a través de un método tenderá a duplicar el número de pruebas que necesito hacer. La pereza pura dicta que después de haber agregado demasiada lógica, y tengo que escribir 16 pruebas para agregar una condición, es hora de extraer parte de ella en otro método / clase y probarlo por separado.

Es realmente tan simple. No es una herramienta de diseño mágico.

    
respondido por el pdr 30.05.2013 - 00:24
6
  

Estoy tratando de envolver mi cabeza alrededor de TDD ...   Para ilustrar, imagine estos 3 requisitos:

     
  • Un catálogo debe tener una lista de productos
  •   
  • El catálogo debe recordar qué productos vio un usuario
  •   

Estos requisitos deben ser reformulados en términos humanos. ¿Quién quiere saber qué productos vio el usuario anteriormente? ¿El usuario? ¿Un vendedor?

  
  • Los usuarios deben poder buscar un producto
  •   

¿Cómo? ¿Por nombre? ¿Por marca? El primer paso en el desarrollo basado en pruebas es definir una prueba, por ejemplo:

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

  

En este punto, muchos libros sacan un conejo mágico de un sombrero.   y simplemente sumergirse en "Probando el Servicio de Producto", pero no lo hacen   explique cómo llegaron a la conclusión de que existe un Servicio de Producto   en primer lugar.

Si estos son los únicos requisitos, ciertamente no saltaría para crear un Servicio de Producto. Podría crear una página web muy simple con una lista de productos estática. Eso funcionaría perfectamente hasta que llegue a los requisitos para agregar y eliminar productos. En ese momento, podría decidir que es más sencillo utilizar una base de datos relacional y un ORM, y crear una clase de Producto asignada a una sola tabla. Todavía no hay ProductService. Las clases como ProductService se crearán cuando sean necesarias. Puede haber múltiples solicitudes web que necesiten realizar las mismas consultas o actualizaciones. Luego, la clase ProductService se creará para evitar la duplicación de código.

En resumen, TDD controla el código que se va a escribir. El diseño sucede a medida que realiza elecciones de implementación, y luego refactoriza el código en clases para eliminar la duplicación y las dependencias de control. A medida que agregue código, deberá crear nuevas clases para mantener el código SÓLIDO. Pero no necesita decidir con anticipación que necesitará una clase de producto y una clase de servicio de producto. Puede encontrar que la vida está perfectamente bien con solo una clase de Producto.

    
respondido por el kevin cline 29.05.2013 - 23:26
3

Otros pueden estar en desacuerdo, pero para mí muchas de las metodologías más nuevas se basan en la suposición de que el desarrollador hará la mayor parte de lo que las metodologías anteriores explicaron por costumbre o orgullo personal, que el desarrollador generalmente está haciendo algo que es bastante obvio para ellos, y el trabajo está encapsulado en un lenguaje limpio o las partes más limpias de un lenguaje un tanto desordenado para que pueda hacer todo el negocio de prueba.

Algunos ejemplos en los que me he encontrado con esto en el pasado:

  • Tome un grupo de contratistas especializados y dígales que su equipo es Agile y prueba primero. A menudo no tienen otro hábito que trabajar para especificaciones y no tienen ninguna preocupación sobre la calidad del trabajo, siempre y cuando dura lo suficiente para terminar el proyecto.

  • Primero intente hacer algo nuevo, pase mucho tiempo grabando Las pruebas a medida que encuentre varios enfoques e interfaces son basura.

  • Codifique algo de bajo nivel y reciba una bofetada por falta de cobertura, o escribe muchas pruebas que no tienen mucho valor porque no puede burlarse de los comportamientos subyacentes a los que está vinculado.

  • Cualquier situación en la que no tenga suficientes de los mecanismos subyacentes antes de tiempo para agregar una función comprobable sin escribir primero un grupo de bits subyacentes no verificables, como un subsistema de disco o una interfaz de comunicación de nivel tcpip.

Si está haciendo TDD y está funcionando para usted, es bueno para usted, pero hay muchas cosas (trabajos completos o etapas de un proyecto) ahí donde esto simplemente no agrega valor.

Su ejemplo suena como si no estuviera aún en un diseño, así que necesita una conversación de arquitectura o está haciendo un prototipo. En mi opinión, debes pasar algo de eso primero.

    
respondido por el Bill 29.05.2013 - 22:09
1

Estoy convencido de que TDD es un enfoque muy valioso para el diseño detallado del sistema, es decir, las API y el modelo de objetos. Sin embargo, para llegar al punto en un proyecto en el que comenzaría a utilizar TDD, debe tener el panorama general del diseño ya modelado de alguna manera y el panorama general de la arquitectura ya modelado de alguna manera. @ user414076 parafrasea a Robert Martin teniendo en mente una idea de diseño, pero no estando casado con ella. Exactamente. Conclusión: TDD no es la única actividad de diseño que se está realizando, es la forma en que se desarrollan los detalles del diseño. TDD debe ir precedido por otras actividades de diseño y encajar en un enfoque general (como Agile) que aborde cómo se crea y evoluciona el diseño general.

FYI: dos libros que recomiendo sobre el tema que dan ejemplos tangibles y realistas:

Creciente software orientado a objetos, guiado por pruebas : explica y ofrece un ejemplo completo del proyecto. Este es un libro sobre diseño, no pruebas . Las pruebas se utilizan como un medio para especificar el comportamiento esperado durante las actividades de diseño.

desarrollo guiado por pruebas Una guía práctica : un recorrido lento y paso a paso para desarrollar un completo , aunque pequeña, app.

    
respondido por el Chuck Krutsinger 05.06.2013 - 22:54
0

El descubrimiento del diseño de las unidades TTD se debe a una falla en la prueba, no al éxito, por lo que puede probar incógnitas y volver a realizar pruebas repetitivas a medida que se exponen las incógnitas, lo que conduce a un arnés completo de pruebas unitarias. Es algo muy bueno para el mantenimiento continuo y una tarea muy difícil. para tratar de modernizar después de que el código se escribe / libera.

Por ejemplo, un requisito puede ser que la entrada pueda estar en varios formatos diferentes, no todos son conocidos todavía. Al usar TDD, primero escribiría una prueba que verifique que la salida apropiada se proporciona dado el formato de entrada cualquier . Obviamente, esta prueba fallará, por lo que escribe código para manejar los formatos conocidos y vuelve a realizar la prueba. Como los formatos desconocidos se exponen a través de la recopilación de requisitos, las nuevas pruebas se escriben antes , el código se escribe, también deberían fallar. Luego se escribe un nuevo código para admitir los nuevos formatos y se ejecutan todas todas las pruebas, lo que reduce las posibilidades de regresión.

También es útil pensar en la falla de la unidad como un código "sin terminar" en lugar de un código "roto". TDD permite unidades sin terminar (fallas esperadas), pero reduce la aparición de unidades rotas (fallas inesperadas).

    
respondido por el Daniel Pereira 29.05.2013 - 22:47
0

En la pregunta se establece:

  

... muchos libros sacan un conejo mágico de un sombrero y simplemente se sumergen en "Testing the ProductService", pero no explican cómo llegaron a la conclusión de que existe un ProductService en primer lugar.

Llegaron a esa conclusión al pensar en cómo iban a probar este producto. "¿Qué tipo de producto hace esto?" "Bueno, podríamos crear un servicio". "Ok, vamos a escribir una prueba para tal servicio"

    
respondido por el Bryan Oakley 30.05.2013 - 01:12
0

Una funcionalidad puede tener muchos diseños y TDD no le dirá completamente cuál es el mejor. Incluso, si las pruebas lo ayudan a construir un código más modular, también lo puede llevar a construir módulos que se ajusten a los requisitos de las pruebas y no a la realidad de producción. Así que tienes que entender a dónde vas y cómo deben encajar las cosas en el cuadro completo. De lo contrario, existen requisitos funcionales y no funcionales, no olvide el último.

En cuanto al diseño, me refiero a los libros de Robert C. Martin (Desarrollo ágil), pero también a los Patrones de arquitectura de aplicaciones empresariales y diseño de controladores de dominio de Martin Fowler. El último es especialmente muy sistemático en la extracción de Entidades y Relaciones de los requisitos.

Luego, cuando tenga una buena idea de las opciones disponibles sobre cómo administrar esas entidades, puede alimentar su enfoque de TDD.

    
respondido por el Ludovic 04.06.2013 - 07:50
0
  

¿No se pretende que TDD me haga descubrir mi diseño en primer lugar?

No.

¿Cómo puedes probar algo que no has diseñado primero?

  

Para ilustrar, imagine estos 3 requisitos:

     
  • Un catálogo debe tener una lista de productos
  •   
  • El catálogo debe recordar qué productos vio un usuario
  •   
  • Los usuarios deben poder buscar un producto
  •   

Estos no son requisitos, son definiciones de datos. No sé cuál es el negocio de su software, pero no es probable que los analistas hablen de esa manera.

Necesitas saber cuáles son las invariantes de tu sistema.

Un requisito sería algo como:

  • Un cliente puede pedir un producto de cierta cantidad, si hay suficiente producto en stock.

Si este es el único requisito, puede tener una clase como:

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

Luego, utilizando TDD, escribiría un caso de prueba antes de implementar el método order ().

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

Entonces, la segunda prueba fallará, entonces puedes implementar el método order () como quieras.

    
respondido por el Guillaume 04.06.2013 - 12:21
0

Usted tiene bastante razón TDD resultará en una buena implementación de un diseño dado. No ayudará a su proceso de diseño.

    
respondido por el James Anderson 06.06.2013 - 03:59
-3

TDD ayuda mucho, sin embargo, hay una parte importante en el desarrollo de software. El desarrollador debe escuchar el código que se está escribiendo. La refactorización es la 3ª parte del ciclo TDD. Este es el paso principal donde el desarrollador debe concentrarse y pensar antes de pasar a la siguiente prueba roja. ¿Hay alguna duplicación? ¿Se aplican los principios de SOLID? ¿Qué pasa con la alta cohesión y el bajo acoplamiento? ¿Qué pasa con los nombres? Observe detenidamente el código que está emergiendo de las pruebas y vea si hay algo que deba ser cambiado, rediseñado. Cuestione el código y el código le dirá cómo se diseñará. Por lo general escribo conjuntos de pruebas múltiples, examino esa lista y creo el primer diseño simple, no necesita ser "final", por lo general no lo es, porque ha cambiado al agregar nuevas pruebas. Ahí es donde viene el diseño.

    
respondido por el Adronius 04.06.2013 - 21:56

Lea otras preguntas en las etiquetas