pruebas - tdd y bdd
Proceso de programación de pseudocódigo vs. desarrollo impulsado por prueba (6)
Con Test Driven Development todavía deberías estar haciendo algo de planificación al principio. Al principio debería ser una mirada de alto nivel a lo que estás tratando de hacer. No se le ocurran todos los detalles, pero tenga una idea clara en inglés de cómo resolver el problema.
Luego comience a probar el problema. Una vez que tenga la prueba en su lugar, comience a hacerla pasar. Si no es fácil de hacer, es posible que deba revisar su plan inicial. Si hay problemas solo revise. La prueba no está ahí para definir la solución que está allí para permitirle hacer cambios para que pueda tener una mejor solución mientras se asegura la estabilidad.
Yo diría que la mejor opción es usar TDD. La clave es darse cuenta de que TDD no significa "omitir la planificación". TDD significa hacer un poco de planificación para comenzar bien, y ajustar según sea necesario. Puede que ni siquiera necesites ajustarte.
Para aquellos que no han leído Code Complete 2, el Proceso de Programación de Pseudocódigo es básicamente una forma de diseñar una rutina describiéndola en inglés simple, luego, gradualmente, la revisa a un pseudocódigo más detallado, y finalmente al código. El principal beneficio de esto es ayudarlo a mantenerse en el nivel correcto de abstracción construyendo sistemas de arriba hacia abajo en lugar de hacia abajo, desarrollando así una API limpia en distintas capas. Encuentro que el TDD es menos efectivo en esto, porque se enfoca demasiado en hacer lo mínimo para hacer pasar una prueba y alienta un pequeño diseño inicial. También considero que tener que mantener un conjunto de pruebas unitarias para código inestable (código que se está refactorizando constantemente) es bastante difícil, porque generalmente ocurre que tiene una docena de pruebas unitarias para una rutina que solo se necesita una o dos veces. Cuando realizas la refactorización, por ejemplo, cambias la firma de un método, la mayor parte del trabajo que realizas es para actualizar las pruebas en lugar del código prod. Prefiero agregar pruebas unitarias después de que el código de un componente se haya estabilizado un poco.
Mi pregunta es: de aquellos que han probado ambos enfoques, ¿cuál prefieres?
El hecho de que la prueba pase, no significa que haya terminado.
TDD se caracteriza mejor por Red - Green - Refactor .
Tener una prueba proporciona una (de dos) líneas de gol. Es solo el primer y mínimo conjunto de requisitos. El objetivo real es el mismo objetivo que el "Proceso de programación de pseudocódigo" o cualquier disciplina de diseño.
Además, el TDD es impulsado por las pruebas, pero eso no significa conducido a ciegas por las pruebas. Puede repetir sus pruebas de la misma manera que itera su código. Aquí no hay lugar para la adhesión dogmática a un plan tonto. Esta es una técnica Agile, eso significa adaptarla a su equipo y sus circunstancias.
Diseña un código suficiente para tener una interfaz comprobable. Diseñe suficientes pruebas para asegurarse de que la interfaz funcione. Diseñe algunas pruebas más y más implementación hasta que vea la necesidad de refactorizar.
El verdadero objetivo es un buen software. TDD no puede excluir "bondad".
Una técnica no es un mandato restrictivo. Las técnicas deben verse como una muleta para ayudarlo a producir un buen código. Si fuera más inteligente, más rico y con mejor aspecto, no necesitaría TDD. Pero como soy tan tonto como soy, necesito una muleta para ayudarme a refactorizar.
En general, creo que el pseudocódigo solo se vuelve relevante cuando el código requerido para resolver el problema es mucho más complicado que el código requerido para probar la solución. Si este no es el caso, no me encuentro con las dificultades que describe ya que lo más simple que podría funcionar es generalmente una solución aceptable para la cantidad de tiempo que vale la pena gastar en el problema.
Si, por otro lado, el problema es complicado, tengo que pensar en cómo abordarlo antes de poder escribir siquiera una solución ingenua inicial; aún tengo que planificar antes de codificar; por lo tanto, utilizo una combinación de ambos enfoques: una descripción en inglés de lo que inicialmente escribiré, luego un arnés de prueba, luego un código ingenuo de solución y luego un refinamiento.
He usado ambos junto con Big Upfront Development, los tres tienen sus lugares dependiendo de cuestiones tales como el idioma, la dinámica del equipo y el tamaño / complejidad del programa.
En los lenguajes dinámicos (particularmente Ruby), recomiendo TDD, lo ayudará a detectar errores que otros lenguajes habrían atrapado en tiempo de compilación.
En un sistema grande y complejo, cuanto más diseño haces por adelantado, mejor. Parece que cuando diseñé para un gran proyecto, cada área que saludé con la mano y dije "esto debería ser bastante directo" fue un punto de tropiezo más adelante en el proyecto.
Si está trabajando solo en algo pequeño en un lenguaje de tipo estático, el enfoque de la lista es razonable y le ahorrará una gran cantidad de tiempo con respecto a TDD (el mantenimiento de la prueba NO es gratuito, aunque escribir las pruebas en primer lugar no es demasiado malo) - Cuando no hay ninguna prueba en el sistema en el que está trabajando, no siempre se admira la adición de pruebas y es posible que incluso atraiga alguna atención no deseada.
Para mí, TDD tiene un as pseudocódigo que no puede competir: ambos te ayudan a abstraer y planear el desarrollo, pero una vez que terminas el desarrollo en tierra TDD , todavía tienes las pruebas unitarias .
Un enfoque tan útil como CC2 describe es la pseudocodificación, simplemente no puede coincidir con eso. TDD solo tiene la mitad de diseño, también proporciona un andamio riguroso del que puede hacer evolucionar el proyecto. Sin embargo, no veo ninguna razón por la cual no puedas pseudocódigo para resolver los problemas que establece TDD.
No debo desarrollarme orgánicamente
El seudocódigo es el asesino de la mente.
Es la pequeña muerte lo que trae el olvido de la memoria del proyecto.
Enfrentaré la metodología de los 90.
Permitiré que pase sobre mí y a través de mí.
Y cuando haya pasado giraré el ojo interno para ver su camino.
Donde se haya ido el pseudocódigo, habrá TDD.
Solo las pruebas unitarias permanecerán.
(Por favor no me llame por eso, solo estoy medio serio: P)
Mi equipo combina ambos enfoques y es una manera increíble de desarrollarse (al menos para nosotros). Necesitamos pruebas unitarias porque tenemos un sistema de software grande y complejo. Pero el proceso de programación de pseudocódigo es el mejor enfoque para el diseño de software que he encontrado. Para que funcionen juntos:
- Comenzamos escribiendo nuestras clases y completando con los stubs de métodos totalmente comentados, con entradas y salidas.
- Usamos la codificación de pares y la revisión por pares como un diálogo para refinar y validar el diseño, aún solo con los stubs del método.
- En este punto, ahora hemos diseñado nuestro sistema y tenemos un código comprobable. Así que seguimos adelante y escribimos nuestras pruebas unitarias.
- Volvemos y comenzamos a completar los métodos con comentarios sobre la lógica que debe escribirse.
- Escribimos código; las pruebas pasan
La belleza de esto es que para cuando escribimos el código, la mayor parte del trabajo de implementación ya está hecho, porque gran parte de lo que pensamos como implementación es en realidad el diseño del código. Además, el proceso inicial reemplaza la necesidad de UML: los apéndices de la clase y del método son tan descriptivos, además de que realmente se usarán. Y siempre nos mantenemos en el nivel apropiado de abstracción.
Obviamente, el proceso nunca es tan lineal como he descrito: algunos cambios en la implementación pueden significar que tenemos que volver a visitar el diseño de alto nivel. Pero en general, cuando redactamos las pruebas unitarias, el diseño es bastante estable (a nivel de método), por lo que no es necesario reescribir muchas pruebas.