2010-08-16 25 views
11

Similar a Does TDD mean not thinking about class design?, estoy teniendo problemas para pensar dónde encaja la etapa tradicional de 'diseño' en TDD.¿Cómo se diseñan sistemas complejos con TDD?

De acuerdo con el Bowling Game Kata (la versión de "conversación", cuyo vínculo se me escapa en este momento) TDD parece ignorar las decisiones de diseño tomadas desde el principio (descartar el objeto marco, rodar objetos, etc.). Puedo ver en ese ejemplo que es una buena idea seguir las pruebas e ignorar sus pensamientos de diseño iniciales, pero en proyectos más grandes o en los que quiere dejar una oportunidad para la expansión/personalización, ¿no sería mejor poner las cosas en su lugar? que no tiene una prueba o no tiene una necesidad de inmediato para evitar reescribir el tiempo después?

En resumen: ¿cuánto diseño es demasiado cuando se hace TDD, y cuánto debería seguir ese diseño mientras escribo las pruebas y el código para pasarlas (ignorando mi diseño para solo preocuparme por pasar las pruebas)?

O no me preocupo por nada, y el código escrito simplemente para seguir las pruebas es no (en la práctica) ¿es difícil de reescribir o refactorizar si está pintado en una esquina? Como alternativa, ¿me falta el punto y debería estar esperando para volver a escribir porciones del código cuando llegue a probar una nueva sección de funcionalidad?

Respuesta

9

Me gustaría basar sus pruebas en su diseño inicial. En muchos sentidos, TDD es un proceso de descubrimiento. Puede esperar confirmar sus primeras opciones de diseño o descubrir que hay mejores opciones que puede tomar. Haga todo el diseño inicial con el que se sienta cómodo. A algunos les gusta volar por el asiento de las sillas haciendo un diseño de alto nivel y usando TDD para pulir el diseño. Mientras que a otros les gusta tener todo en papel primero.

Parte de TDD está refactorizando.

+3

+1 para 'Parte de TDD está refactorizando': El diseño deberá actualizarse para reflejar los cambios que se encuentran necesarios durante la prueba. –

+0

Gracias, entonces me falta el punto, y la refactorización/reescritura es parte de este diseño emergente. ¡Es bueno saberlo, ahora probarlo con algo! –

3

Comience con una idea de diseño aproximado, elija una primera prueba y comience la codificación, yendo a la prueba verde tras la prueba, dejando que surja el diseño, similar o no al diseño inicial. Cuánto de diseño inicial depende de la complejidad del problema.

Uno debe estar atento y escuchar y oler el código, para detectar oportunidades de refactorización y códigos de olores.

Seguir estrictamente a TDD y al SOLID principles hará que el código sea limpio, comprobable y flexible, de modo que pueda refactorizarse fácilmente, aprovechando las pruebas unitarias como andamios para evitar la regresión.

+0

Entonces, ¿el diseño inicial está más allí en caso de que el diseño emergente se tope con una pared de ladrillos (o parece que está a punto de hacerlo)? –

+1

El diseño inicial es para comenzar, para dar la primera dirección, para el problema, o una parte del problema. El diseño se crea incrementalmente. – philant

+0

"Escribir primero la prueba" sin tener la menor idea de lo que se debe hacer, termina produciendo pruebas correctas para el problema equivocado. En primer lugar, es necesario comprender el dominio del problema y los requisitos arquitectónicos, porque estamos hablando de problemas complejos, luego emplear pequeños prototipos para alimentar su imaginación con ideas y poner estas ideas bajo escrutinio. Solo necesita una herramienta de creación de prototipos como IPython o Jupyter en la mayoría de los casos o una prueba de concepto en casos más elaborados, con el objetivo de validar los requisitos de la empresa. Finalmente, escribe el código y escribe pruebas para él. –

2

que he encontrado tres formas de hacer el diseño con TDD:

  • permiten el diseño emerja de forma natural como la duplicación y la complejidad se retira
  • Crear un diseño perfecto por adelantado, usando burla combinado con la principio de responsabilidad única
  • Sea pragmático al respecto.

Pragmatismo parece ser la mejor opción la mayoría de las veces, así que esto es lo que hago. Si sé que un patrón en particular se adaptará muy bien a mi problema (por ejemplo, MVC) voy directamente a los simulacros y supongo que funciona. De lo contrario, si el diseño es menos claro, permitiré que surja.

El punto de cruce en el que siento la necesidad de refactorizar un diseño emergente es el punto en el que deja de ser fácil de cambiar. Si un fragmento de código no está perfectamente diseñado, pero otro desarrollador encontrado podría fácilmente refactorizarlo, es suficiente.Si el código se vuelve tan complejo que deja de ser obvio para otro desarrollador, es hora de refactorizarlo.

Me gustan las opciones reales, y la refacturación de algo a la perfección me parece que me comprometo con el diseño sin ninguna necesidad real de hacerlo. Me refactorio a "lo suficientemente bueno" en su lugar; De esa forma, si mi diseño demuestra ser incorrecto, no he perdido el tiempo. Suponga que su diseño será incorrecto si nunca lo ha usado antes en un contexto similar.

Esto también me permite obtener mi código mucho más rápido que si fuera perfecto. Habiendo dicho eso, ¡fue mi intento de hacer el código perfecto que me enseñó dónde estaba la línea!

7

Hay algo que decir acerca de 'Diseñar sistemas complejos grandes' que no debería asociarse con TDD - especialmente cuando TDD se interpreta como 'Diseño controlado por prueba' y no 'Desarrollo controlado por prueba'.

En el contexto 'Desarrollo', usando TDD se asegurará de que está escribiendo comprobable código que dan todos los beneficios citados sobre TDD (detectar errores tempranos, código de alta: índice de cobertura de la prueba, el futuro más fácil refactorización, etc, etc)

Pero en grandes sistemas complejos 'Proyectos', TDD particularmente no abordan las siguientes cuestiones que son inherentes a la arquitectura del sistema

  1. (Ingeniería de) Rendimiento
  2. Seguridad
  3. Escalabilidad
  4. disponibilidad
  5. (y todos los demás 'ilities')

(es decir, todas las preocupaciones anteriores no surgen mágicamente a través de "escribir primero un caso de prueba fallido, seguido de la implementación de trabajo, Refactorizar - enjabona, enjuaga, repite ..." receta).

  • Para estos, que se necesitan para abordar el problema de color blanco de abordar el alto nivel y luego detalles de bajo nivel de un sistema con respecto a las restricciones impuestas por los requisitos y el espacio del problema.

  • Algunas de las consideraciones anteriores compiten entre sí y requieren compensaciones cuidadosas que simplemente no 'emergen' mediante la escritura de muchas pruebas unitarias.

  • vez que los componentes clave y sus responsabilidades se definen y comprendidos, TDD se puede usar en la aplicación de estas componentes. El proceso de refactorización y la revisión o mejora continua de su código garantizarán que los detalles de diseño de bajo nivel de estos componentes estén bien elaborados.

sin embargo, yo he de venir a través de una pieza significativamente compleja del software (por ejemplo, compilador, base de datos, sistema operativo) que se hizo en un estilo Driven Design prueba .El siguiente artículo de blog habla sobre este punto extremadamente bien (Compilers, TDD, Mastery)

Además, consulte el siguiente videos on Architecture que agrega mucho sentido común al proceso de pensamiento.

+0

En realidad, estoy desarrollando un clon de la pila Asp.Net MVC, similar a vNext pero basado en corrutinas, y me estoy enfadando tratando de probar todo. En realidad, principalmente hago pruebas de integración y pruebas unitarias solo para el componente más pequeño, como para las páginas de renderizado o maquinilla de afeitar o el manejador de roouting. Al ser un entorno multiproceso, preparé una especie de "hilo falso" para ejecutar todas las partes paso a paso. Sé que no estoy haciendo una TDD estricta, pero sería IMHO un poco imposible para una "banda de un solo hombre". – EDR

+0

De lejos, la respuesta más sensata a la pregunta. Los defensores de TDD evangelizan: "¡Escriba primero las pruebas!" ... pero ... ¿cómo puede escribir pruebas si no tiene nada, ni siquiera la más mínima idea de lo que se debe hacer? ¡Escribirás pruebas correctas que resuelven el problema incorrecto o no resuelven nada necesario! Primero, comprenda el dominio del problema, escriba un prototipo dentro de Jupyter, por ejemplo, evalúe su rendimiento, huella de memoria, capacidad de escalado y otros requisitos; si es necesario, escriba una prueba de concepto; luego abrazar TDD con el conocimiento que adquiriste en la fase de creación de prototipos. –

Cuestiones relacionadas