2009-12-31 7 views
15

Tengo una buena comprensión de las pruebas unitarias, DI, simulacros y todas las virtudes principales del diseño requeridas para tener una cobertura del código lo más humanamente posible (principal de responsabilidad única, piense 'cómo voy a probar esto' a medida que codigo, etc ...).diseño de aplicaciones de tamaño medio cuando se hace TDD?

Mi aplicación más reciente, no codifiqué haciendo true TDD. Mantuve las pruebas unitarias en mente cuando codifiqué, y escribí mis pruebas después de escribir el código, refactorizar, etc. Hice TDD cuando era "fácil" de hacer ... sin embargo no tenía tan buena comprensión como Ahora sí ... Ese fue el primer proyecto en el que hice uso completo de DI, frameworks de burla, etc., y el primero que tenía una cobertura completa de código, y aprendí mucho de él a medida que avanzaba. Estoy ansioso por ser asignado a mi próximo proyecto, así que puedo codificarlo completamente haciendo TDD desde cero.

Sé que esta es una pregunta amplia, y ya he ordenado TDD con el ejemplo y XP Unleashed, pero espero obtener una breve descripción de cómo todos ustedes diseñan/escriben una aplicación grande que hace TDD.

¿Escribes toda la aplicación, usando solo el código apagó? (por ejemplo, escriba todas las firmas de funciones, interfaces, estructuras y escriba la aplicación completa pero sin escribir ninguna implementación real)? Podría imaginarlo trabajando en tamaño pequeño-medio, ¿pero esto es posible incluso en aplicaciones grandes?

Si no, ¿cómo diablos escribiría su primera prueba de unidad para la función de nivel más alto en su sistema? Digamos, por ejemplo, en un servicio web en el que tiene una función llamada DoSomethingComplicated (param1, ..., param6) expuesta al mundo. Obviamente, escribir la prueba primero para una función simple como AddNumbers() es trivial, pero cuando la función está en la parte superior de la pila de llamadas como esta?

¿Todavía tiene diseño frontal? Obviamente, todavía desea hacer un diseño de 'arquitectura', por ejemplo, un diagrama de flujo que muestra IE hablando con IIS que habla con un servicio de Windows vía WCF que habla con la base de datos SQL ... un ERD que muestra todas sus tablas SQL y sus campos. etc ... pero ¿y el diseño de clase? Interacciones entre las clases, etc. ¿Diseñas esto por adelantado, o simplemente sigues escribiendo código de código auxiliar, refactorizando las interacciones a medida que avanzas, hasta que todo se conecta y parece que funcionará?

Cualquier consejo es muy apreciada

Respuesta

12

¿Escribo toda la aplicación, usando solo el código apagó?

No, en el sentido más leve, suena como un enfoque muy derrochador. Siempre debemos tener en cuenta que la razón subyacente para hacer TDD es retroalimentación rápida. Un conjunto de pruebas automatizadas puede decirnos si rompimos algo mucho más rápido que una prueba manual.Si esperamos cablear las cosas juntas hasta el último momento, no obtenemos retroalimentación rápida; aunque podemos obtener una respuesta rápida de nuestras pruebas unitarias, no sabríamos si la aplicación funciona como un todo. Las pruebas unitarias son solo una forma de prueba que debemos realizar para verificar la aplicación.

Un mejor enfoque es comenzar con la característica más importante y su forma de trabajo a partir de ahí, el uso de un de afuera hacia adentro enfoque. Esto a menudo significa comenzar con alguna UI.

La forma en que lo hago es creando la interfaz de usuario deseada. Como normalmente no podemos desarrollar la IU con TDD, simplemente creo la Vista con la tecnología que elijas. No hay pruebas allí, pero conecto la interfaz de usuario a alguna API (preferiblemente usando el enlace de datos declarativo), y es entonces cuando comienzan las pruebas.

Al principio, yo solía TDD mi ViewModels/Presentation Models y los Controladores correspondientes, posiblemente codificando algunas respuestas para ver que la UI funciona. Tan pronto como tenga algo que no explota cuando lo ejecute, verifico el código (recuerde, muchos check-ins incrementales pequeños).

A continuación, funciono verticalmente abajo esa característica y me aseguro de que esta pieza particular de UI pueda llegar hasta la fuente de datos (o lo que sea), ignorando todas las demás características.

Cuando la función está lista, puedo comenzar con la siguiente función. La forma en que imagino este proceso es que llene la aplicación haciendo un corte vertical a la vez hasta que todas las funciones estén hechas.

Arrancar una aplicación greenfield de esta manera siempre lleva tiempo extra para la primera característica ya que aquí es donde tiene que conectar todo, así que elija algo simple (como la Vista inicial de la aplicación) para mantener las cosas lo más sencillo posible. Una vez que se realiza la primera función, los siguientes se vuelven mucho más fáciles porque las bases están ahora en su lugar.

¿Todavía diseño frontal?

No mucho, no. Normalmente, tengo un diseño general en mente antes de comenzar, y cuando trabajo en equipo, esbozamos esta arquitectura general en una pizarra blanca o una plataforma de diapositivas antes de comenzar.

Esto es más o menos limitado a

  • El número y los nombres de las capas (IU, Presentación lógica, modelo de dominio, acceso a datos, etc).
  • las tecnologías utilizadas (WPF, ASP.NET MVC, SQL Server, .NET 3.5 o lo que sea)
  • Cómo estructuramos el código de producción y el código de prueba, y las tecnologías que utilizamos
  • Requisitos de calidad para el código de prueba (programación de par, análisis de código estático, estándares de codificación, etc.)

El resto lo descubrimos a medida que avanzamos, pero usamos muchas sesiones de diseño ad-hoc en la pizarra a medida que avanzamos.

+0

Gracias por la excelente y detallada respuesta. Suena muy parecido a cómo codifiqué mi última aplicación ... (excepto que "engañé" al escribir las pruebas primero en la mayoría de los lugares) Tal vez la transición de las pruebas unitarias a TDD realmente no cambia la forma en que diseño y trabajo tanto como yo Pensé ... Así que tengo que preguntar: ¿eres muy estricto con las pruebas primero? ¿Alguna vez encontró eso simplemente, es más fácil escribir el código antes de la prueba? Si es así, ¿qué tan común sucede esto? Y en cualquier patrón/situación específica? – dferraro

+2

Soy muy estricto en lo que respecta a la prueba, ya que considero que la fase * Roja * de Red/Green/Refactor es muy importante. A menudo he escrito una prueba que resultó verde inmediatamente, a pesar de la intención opuesta. Esto significa que la prueba no prueba lo que pensé que estaba probando, por lo que debe ser reescrito. Esto me pasa a mí aproximadamente. una vez al día, pero esta protección no está disponible cuando usted escribe las pruebas después. Muy pocas veces aumente sin pruebas, pero cuando eso sucede, borro el código de spike cuando termino, y luego la implementación correcta de TDD en base a mi experiencia de spike. –

1

1 Buena pregunta

yo realmente no sé la respuesta, pero me gustaría empezar con bloques de construcción de las clases que pude probar luego construir en la aplicación, no se con las cosas de alto nivel. Y sí, tendría un diseño inicial rudo de las interfaces, de lo contrario, creo que encontraría esas interfaces cambiando tan a menudo como refactorizaría que sería un obstáculo real.

TDD Por ejemplo no ayudará No creo. IIRC pasa por un simple ejemplo. Estoy leyendo The Art of Unit Testing de Roy Osherove y si bien parece abarcar exhaustivamente herramientas y técnicas como simulacros y trozos, el ejemplo hasta ahora parece también bastante simple y no veo que te indique cómo abordar un gran proyecto.

19
  • ¿Hace usted diseñar al frente?

Por supuesto que sí. Tienes una gran aplicación frente a ti.Debe tener una idea de la estructura que tendrá antes de comenzar a escribir pruebas y códigos. No tiene que tener todo resuelto en detalle, pero debe tener una idea básica de las capas, componentes e interfaces. Por ejemplo, si está trabajando en un sistema de servicios web, debe saber cuáles son los servicios de nivel superior y tener una buena primera aproximación de sus firmas.

  • ¿Escribes la aplicación completa usando solo el código apagó?

No. Resuelve cosas solo si son realmente difíciles de controlar en una prueba. Por ejemplo, me gusta rescindir la base de datos y la interfaz de usuario. También tropezaré con interfaces de terceros. A veces anularé uno de mis propios componentes si aumenta enormemente el tiempo de prueba, o me obliga a crear datos de prueba que son demasiado complicados. Pero la mayor parte del tiempo dejo que mis pruebas funcionen en un sistema bastante bien integrado.

Tengo que decir que realmente no me gusta el estilo de prueba que se basa en gran medida en simulacros y trozos. No me malinterpreten, creo que los simulacros y los talones son muy útiles para desvincularse de cosas que son difíciles de probar. Pero no me gusta escribir cosas que son difíciles de probar, así que no uso muchos simulacros y trozos.

  • ¿Cómo se escribe la primera unidad de prueba para una función de alto nivel?

La mayoría de las funciones de alto nivel tienen un comportamiento degenerado. Por ejemplo, iniciar sesión es una función de alto nivel y puede ser muy complicado. Pero si intenta iniciar sesión sin nombre de usuario y sin contraseña, la respuesta del sistema será bastante simple. Escribir que las pruebas también serán muy simples. Entonces comienzas con los casos degenerados. Una vez que los haya agotado, pasará al siguiente nivel de complejidad. Por ejemplo, ¿qué pasa si un usuario intenta iniciar sesión con un nombre de usuario pero sin contraseña? Poco a poco subes la escalera de la complejidad, sin abordar los aspectos más complejos hasta que los aspectos menos complejos pasan.

Es notable lo bien que funciona esta estrategia. Podrías pensar que estarías trepando por los bordes todo el tiempo y nunca llegarías a la carne; pero eso no es lo que sucede. En su lugar, se encuentra diseñando la estructura interna del código en función de todos los casos degenerados y excepcionales. Cuando finalmente consigue alrededor de la corriente principal, se encuentra que la estructura del código que está trabajando tiene un buen agujero de tan sólo la forma correcta para tapar el flujo principal en.

  • Por favor, no crear su UI primero.

Las IU son engañosas. Te hacen enfocarte en los aspectos incorrectos del sistema. En su lugar, imagine que su sistema debe tener muchas IU diferentes. Algunos serán web, algunos serán clientes gruesos, algunos serán texto puro. Diseña tu sistema para que funcione correctamente independientemente de la interfaz de usuario. Haga que todas las reglas de negocio funcionen primero, con todas las pruebas aprobadas. Luego, conecta la IU más tarde. Sé que esto va en contra de la sabiduría convencional, pero no lo haría de otra manera.

  • Por favor, no diseñe la base de datos primero.

Las bases de datos son detalles. Guarde los detalles para más tarde. Más bien, diseñe su sistema como si no tuviera idea del tipo de base de datos que estaba usando, mantenga fuera del núcleo del sistema cualquier noción de esquema, tablas, filas y columnas. Implemente las reglas de su negocio como si todos los datos se mantuvieran en la memoria todo el tiempo. A continuación, agregue la base de datos más adelante, una vez que haya logrado que funcionen todas las reglas comerciales.Una vez más, sé que esto va en contra de la sabiduría convencional, pero acoplar los sistemas a las bases de datos demasiado pronto es una fuente de muchos diseños mal deformados.

+0

Awesome reply. Gracias de nuevo – dferraro

+1

"Por favor, no cree primero su UI". Estoy totalmente en desacuerdo. Sin UI (estática) primero, es posible que simplemente esté codificando algo incorrecto. Otra buena lectura: http://www.codinghorror.com/blog/2008/04/ui-first-software-development.html –

+0

Otra buena respuesta: http://stackoverflow.com/questions/1554419/when-i- proceder-a-desarrollar-un-software-ui-diseño-o-base-de-diseño-que-debería –

1
  • se escribe toda la aplicación, usando nada más que apagó el código?

Para probar nuestros sistemas, principalmente realizamos pruebas de unidad, integración y servicios remotos. En pruebas unitarias, eliminamos todos los servicios externos de larga ejecución, que consumen mucho tiempo, es decir, operaciones de bases de datos, conexión de servicios web o cualquier conexión a servicios externos. Esto es para asegurarnos de que nuestras pruebas sean rápidas, independientes y no dependan de la respuesta de ningún servicio externo para proporcionarnos una respuesta rápida. Hemos aprendido esto de la manera difícil porque tenemos algunas pruebas que hacen que las operaciones de la base de datos sean realmente lentas y vayan en contra del principio "Las pruebas unitarias deben ejecutarse rápidamente"

En pruebas de integración, probamos las operaciones de la base de datos pero aún no los servicios web y los servicios externos porque eso puede hacer que la prueba sea frágil dependiendo de su disponibilidad y usamos autotest para ejecutar las pruebas en segundo plano mientras codificamos.

Sin embargo, para probar cualquier tipo de servicios remotos, tenemos pruebas que se conectan a los servicios externos, hacer la operación en ellos y obtener la respuesta. Lo que importa para la prueba es su respuesta y su estado final si es importante para la prueba. Lo importante aquí es que mantenemos este tipo de pruebas en otro directorio llamado remoto (esa es una convención que creamos y seguimos) y estas pruebas remotas solo las ejecuta nuestro servidor de CI (integración continua) cuando fusionamos cualquier código con el maestro/la sucursal principal y lo envía/confirma al repositorio para que sepamos rápidamente si ha habido algún cambio en esos servicios externos que pueda afectar nuestra aplicación.

  • Do I still design up-front?

Sí, pero no hacemos un gran diseño al frente básicamente lo que dijo el tío Bob (Robert C. Martin). Además, llegamos a la pizarra antes de sumergirnos en la codificación y crear algunos Diagramas de colaboración de clase solo para dejar en claro y seguro que todos en el equipo están en sintonía y esto también nos ayuda a dividir el trabajo entre los miembros del equipo .

Cuestiones relacionadas