Estoy tratando de entender 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, Rojo / Verde / Refactor y cómo crear una Calculadora de cadenas.
Cosas buenas, pero eso es "solo" Prueba de Unidad, 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 necesita tener una lista de productos.
- El catálogo debe recordar qué productos ha visto un usuario
- Los usuarios deberían poder buscar un producto
En este punto, muchos libros sacan un conejo mágico de un sombrero y simplemente se sumergen en "Probar el Servicio del Producto", pero no explican cómo llegaron a la conclusión de que hay un Servicio del Producto en primer lugar. Esa es la parte "Desarrollo" en TDD que estoy tratando de entender.
Es necesario que exista un diseño existente, pero no se encuentran elementos fuera de los servicios de la entidad (es decir: hay un producto, por lo que debe haber un servicio de producto) (por ejemplo, el segundo requisito requiere que tenga algún concepto de un Usuario, pero ¿dónde pondría la funcionalidad para recordar? ¿Y la búsqueda es una característica del ProductService o un SearchService separado? ¿Cómo sabría cuál debo elegir?)
Según SOLID , necesitaría un servicio de usuario, 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 desarrollador de .net, pero los recursos de Java también funcionarían. Siento que no parece haber una aplicación o libro de muestra real que trate con una línea real de aplicación comercial. ¿Alguien puede proporcionar un ejemplo claro que ilustre el proceso de creación de un diseño usando TDD?
Respuestas:
La idea de TDD es comenzar con las pruebas y trabajar a partir de eso. Por lo tanto, tomar su ejemplo de "Un catálogo necesita tener una lista de productos" podría considerarse como una prueba de "Buscar productos en el 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 algunos fragmentos que serían algo así como un ProductService que nacerá de pasar esa primera prueba.
La idea de TDD es comenzar con una prueba y luego escribir el código que hace pasar esa prueba como primer punto. Las pruebas unitarias son parte de este sí, pero no está mirando 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.
Test Driven Development Tutorial donde las diapositivas 20-22 son las claves. La idea es saber qué debería hacer la funcionalidad como resultado, escribir una prueba para ello y luego crear una solución. La parte de diseño variará ya que, dependiendo de lo que se requiera, puede o no ser tan simple de hacer. Un punto clave es usar TDD desde el principio en lugar de intentar introducirlo tarde en un proyecto. Si comienza con las pruebas primero, esto puede ayudar y es probable que valga la pena señalarlo en cierto sentido. Si intenta agregar las pruebas más tarde, se convierte en algo que puede posponerse o retrasarse. Las diapositivas posteriores también pueden ser útiles.
Un beneficio principal de TDD es que al comenzar con las pruebas, inicialmente no está bloqueado en un diseño. Por lo tanto, la idea es construir las pruebas y crear el código que pasará esas pruebas como metodología de desarrollo. Un gran diseño inicial puede causar problemas, ya que esto da la idea de bloquear las cosas en su lugar, lo que hace que el sistema que se está construyendo sea menos ágil al final.
Robert Harvey agregó esto en los comentarios que vale la pena indicar en la respuesta:
fuente
Por lo que vale, TDD me ayuda a llegar al mejor diseño más rápido que no hacer TDD. Probablemente llegaría al mejor diseño con o sin él. Pero ese tiempo que habría pasado pensando y analizando el código se dedica a escribir pruebas. Y es menos tiempo. Para mi. No para todos. E, incluso si tomara la misma cantidad de tiempo, me dejaría con un conjunto de pruebas, de modo que la refactorización sería más segura, lo que llevaría a un código aún mejor en el futuro.
Como lo hace
Primero, me anima a pensar en cada clase como un servicio para algún código de cliente. Mejor código viene de pensar en cómo el código de llamada quiere usar la API en lugar de preocuparse por cómo debería verse el 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 la cantidad de pruebas que necesito hacer. La pura pereza dicta que después de haber agregado demasiada lógica, y tengo que escribir 16 pruebas para agregar una condición, es hora de sacar parte de ella en otro método / clase y probarla por separado.
Es realmente así de simple. No es una herramienta de diseño mágico.
fuente
Estos requisitos deben reexpresarse en términos humanos. ¿Quién quiere saber qué productos ha visto el usuario anteriormente? ¿El usuario? ¿Un vendedor?
¿Cómo? ¿Por nombre? Por marca El primer paso en el desarrollo basado en pruebas es definir una prueba, por ejemplo:
>
Si estos son los únicos requisitos, ciertamente no saltaría a crear un ProductService. Podría crear una página web muy simple con una lista de productos estática. Eso funcionaría perfectamente hasta llegar a los requisitos para agregar y eliminar productos. En ese punto, podría decidir que es más simple usar una base de datos relacional y un ORM, y crear una clase de Producto asignada a una sola tabla. Todavía no hay servicio de producto. Las clases como ProductService se crearán cuando sea necesario. Puede haber múltiples solicitudes web que necesiten realizar las mismas consultas o actualizaciones. Luego, se creará la clase ProductService para evitar la duplicación de código.
En resumen, TDD controla el código que se va a escribir. El diseño ocurre a medida que realiza elecciones de implementación y luego refactoriza el código en clases para eliminar la duplicación y controlar las dependencias. A medida que agregue código, deberá crear nuevas clases para mantener el código SOLIDO. 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.
fuente
ProductService
entonces. Pero, ¿cómo le dijo TDD que necesitaba una base de datos y un ORM?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 más antiguas explican solo por hábito u 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 en las partes más limpias de un lenguaje un tanto desordenado para que pueda hacer todo el trabajo de prueba.
Algunos ejemplos en los que me he encontrado con esto en el pasado:
Tome un grupo de contratistas de trabajos especiales y dígales que su equipo es Agile y Test First. A menudo no tienen otro hábito que no sea trabajar según las especificaciones y no les preocupa la calidad del trabajo mientras dure lo suficiente como para terminar el proyecto.
Intente hacer una prueba nueva primero, dedique gran parte de su tiempo a analizar las pruebas ya que encuentra que varios enfoques e interfaces son una mierda.
Codifique algo de bajo nivel y sea abofeteado por falta de cobertura, o escriba muchas pruebas que no sumen mucho valor porque no puede burlarse de los comportamientos subyacentes a los que está vinculado.
Si está haciendo TDD y está funcionando para usted, bien por usted, pero hay muchas cosas (trabajos completos o etapas de un proyecto) en el que esto simplemente no agrega valor.
Su ejemplo parece que aún no tiene un diseño, por lo que necesita tener una conversación de arquitectura o realizar un prototipo. Necesitas superar algo de eso primero en mi opinión.
fuente
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 usar TDD, debe tener una imagen general del diseño ya modelada de alguna manera y debe tener una imagen general de la arquitectura ya modelada de alguna manera. @ user414076 parafrasea a Robert Martin por tener una idea de diseño en mente, pero no estar casada con ella. Exactamente. Conclusión: TDD no es la única actividad de diseño en curso, es cómo se desarrollan los detalles del diseño. El TDD debe estar precedido por otras actividades de diseño y adaptarse a 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 de proyecto. Este es un libro sobre diseño, no sobre 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 una aplicación completa, aunque pequeña.
fuente
TTD impulsa el descubrimiento de diseño por falla de prueba, no por éxito, por lo tanto, puede probar incógnitas y volver a probar de forma iterativa a medida que las incógnitas están expuestas, lo que finalmente conduce a un arnés completo de pruebas unitarias, algo muy bueno para el mantenimiento continuo y algo muy difícil de intentar. actualizar después de que el código se escribe / libera.
Por ejemplo, un requisito puede ser que la entrada puede estar en varios formatos diferentes, aún no se conocen todos. Usando TDD, primero escribiría una prueba que verifique que se proporciona la salida adecuada dado cualquier formato de entrada. Obviamente, esta prueba fallará, por lo que debe escribir código para manejar los formatos conocidos y volver a realizar la prueba. Como los formatos desconocidos se exponen a través de la recopilación de requisitos, se escriben nuevas pruebas antes de que se escriba el código, estas también deberían fallar. Luego, se escribe un nuevo código para admitir los nuevos formatos y se vuelven a ejecutar todas las pruebas, lo que reduce la posibilidad de regresión.
También es útil pensar en la falla de la unidad como código "inacabado" en lugar de código "roto". TDD permite unidades sin terminar (fallas esperadas), pero reduce la aparición de unidades rotas (fallas inesperadas).
fuente
En la pregunta se afirma:
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 ese servicio"
fuente
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 más código modular, también puede llevarlo a crear módulos que se adapten a los requisitos de las pruebas y no a la realidad de producción. Por lo tanto, debe comprender a dónde va y cómo deben encajar las cosas en la imagen completa. Dicho de otro modo, hay requisitos funcionales y no funcionales, no olvide el último.
Con respecto 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 especialmente es muy sistemático en la extracción de las Entidades y Relaciones fuera de los requisitos.
Luego, cuando tenga una buena idea de las opciones disponibles para usted sobre cómo administrar esas entidades, puede alimentar su enfoque TDD.
fuente
No.
¿Cómo puedes probar algo que no has diseñado primero?
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.
Necesita saber cuáles son los invariantes de su sistema.
Un requisito sería algo como:
Entonces, si este es el único requisito, puede tener una clase como:
Luego, usando TDD, escribiría un caso de prueba antes de implementar el método order ().
Entonces, la segunda prueba fallará, luego puede implementar el método order () de la manera que desee.
fuente
Estás en lo cierto, TDD dará como resultado una buena implementación de un diseño dado. No ayudará a su proceso de diseño.
fuente
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 tercera parte del ciclo TDD. Este es el paso principal donde el desarrollador debe enfocarse y pensar antes de pasar a la siguiente prueba roja. ¿Hay alguna duplicación? ¿Se aplican los principios SÓLIDOS? ¿Qué pasa con la alta cohesión y el bajo acoplamiento? ¿Qué hay de los nombres? Eche un vistazo más de cerca al código que está surgiendo de las pruebas y vea si hay algo que necesita ser cambiado, rediseñado. Pregunta el código y el código te dirá, cómo quiere que se diseñe. Por lo general, escribo conjuntos de pruebas múltiples, examino esa lista y creo un primer diseño simple, no es necesario que sea "final", por lo general no lo es, porque cambia al agregar nuevas pruebas. Ahí es donde viene el diseño.
fuente