software que mock informatica tdd mocking

tdd - que - mock software



Cómo evitar la creación de diseños pobres con TDD. (7)

Al igual que una respuesta general a los problemas que tiene, parece que no ha estado usando TDD por mucho tiempo, es posible que no esté usando ninguna herramienta que pueda ayudar con el proceso de TDD, y le está dando más valor a la Línea de código de producción que la línea de código de prueba.

Más específicamente a cada punto:

1: TDD fomenta un diseño que no hace ni más ni menos que lo que tiene que hacer, también conocido como enfoque "YAGNI" (No lo vas a necesitar). Eso es "hazlo ligero". Debe equilibrar eso con "hágalo bien", que consiste en incorporar los conceptos y patrones de diseño SOLID adecuados en el sistema. Tomo la siguiente regla de oro: en el primer uso de una línea de código, haga que funcione. En la segunda referencia a esa línea, hágala legible. En el tercero, hazlo SÓLIDO. Si una línea de código solo es utilizada por otra línea de código, no tiene mucho sentido en ese momento poner un diseño completamente SÓLIDO, dividiendo el código en una clase abstraída de interfaz que se pueda conectar e intercambiar afuera. Sin embargo, debes tener la disciplina para volver atrás y refactorizar el código una vez que comience a obtener otros usos. El diseño de TDD y Agile es TODO sobre la refactorización. Aquí está el problema; también lo es Waterfall, solo cuesta más porque tienes que volver a la fase de diseño para realizar el cambio.

2: De nuevo, esto es disciplina. Principio de responsabilidad única: un objeto debe hacer una cosa específica y ser el único objeto en el sistema que hace esa cosa. TDD no te permite ser perezoso; simplemente te ayuda a descubrir dónde PUEDES ser perezoso. Además, si necesita crear muchos simulacros parciales de una clase, o muchos simulacros completos con muchas funciones, probablemente esté diseñando los objetos y las pruebas de forma incorrecta; sus objetos son demasiado grandes, su SUT tiene demasiadas dependencias y / o el alcance de su prueba es demasiado amplio.

3: No, no lo hace. Requiere que pienses en lo que necesitarás mientras escribes tu suite de prueba. Aquí es donde realmente brillan los asistentes de refactorización como ReSharper (para MSVS); Alt + Enter es su acceso directo "hazlo". Digamos que usted es TDD, una nueva clase que escribirá un archivo de informe. Lo primero que haces es crear una instancia de esa clase. "Espera", se queja ReSharper, "¡No puedo encontrar esa clase!". "Así que créalo", dices, presionando Alt + Enter. Y lo hace así; ahora tienes una definición de clase vacía. Ahora escribes un método llamado en tu prueba. "Espera", grita ReSharper, "ese método no existe", y dices "luego créelo" con otra presión de Alt + Enter. Usted acaba de programar por prueba; Tienes la estructura esquelética para tu nueva lógica.

Ahora, necesitas un archivo para escribir. Comience escribiendo un nombre de archivo como un literal de cadena, sabiendo que cuando RS se queja, simplemente puede decirle que agregue el parámetro a la definición del método. Espera, eso no es una prueba de unidad. Eso requiere el método que está creando para tocar el sistema de archivos, y luego tiene que recuperar el archivo y revisarlo para asegurarse de que sea correcto. Entonces, decides pasar un Stream en su lugar; que le permite pasar un MemoryStream, que es perfectamente compatible con la prueba de la unidad. Ahí es donde TDD influye en las decisiones de diseño; en este caso, la decisión es hacer que la clase sea más SÓLIDA por adelantado para que se pueda probar. La misma decisión le brinda la flexibilidad de canalizar los datos a cualquier lugar que desee en el futuro; en la memoria, un archivo, a través de la red, una tubería con nombre, lo que sea.

4: Un equipo Agile programa por acuerdo. Si no hay acuerdo, eso es un bloque; Si el equipo está bloqueado, no se debe escribir ningún código. Para resolver el bloqueo, el líder del equipo o el gerente del proyecto toma una decisión de comando. Esa decisión es correcta hasta que se demuestre lo contrario; Si termina mal, debería hacerlo rápidamente, para que el equipo pueda ir en una nueva dirección sin tener que retroceder. En su caso específico, haga que su gerente tome una decisión (Rhino, Moq, lo que sea) y la aplique. Cualquiera de eso será un mil por ciento mejor que las pruebas de escritura a mano.

5: Esta debería ser la verdadera fortaleza de TDD. Tienes una clase; su lógica es un desastre, pero es correcta y puede probarlo ejecutando las pruebas. Ahora, comienzas a refactorizar esa clase para que sea más SÓLIDO. Si la refactorización no cambia la interfaz externa de los objetos, entonces las pruebas ni siquiera tienen que cambiar; solo estás limpiando una lógica de método que a las pruebas no les importa, excepto que funciona. Si cambia la interfaz, entonces cambia las pruebas para hacer llamadas diferentes. Esto requiere disciplina; es muy fácil destruir una prueba que ya no funciona porque el método que se está probando no existe. Pero debe asegurarse de que todo el código en su objeto todavía se ejerce adecuadamente. Una herramienta de cobertura de código puede ayudar aquí, y puede integrarse en un proceso de compilación de CI y "romper la compilación" si la cobertura no está a la altura. Sin embargo, la otra cara de la cobertura es en realidad doble: primero, una prueba que agregue cobertura por el bien de la cobertura es inútil; cada prueba debe probar que el código funciona como se espera en una situación novedosa. Además, "cobertura" no es "ejercicio"; sus suites de prueba pueden ejecutar cada línea de código en el SUT, pero no pueden probar que una línea de lógica funciona en cada situación.

Dicho todo esto, hubo una lección muy poderosa sobre lo que TDD hará y no hará cuando me enteré por primera vez. Era un dojo de codificación; la tarea consistía en escribir un analizador de números romanos que tomaría una cadena de números romanos y devolvería un entero. Si entiendes las reglas de los números romanos, esto es fácil de diseñar por adelantado y puede pasar cualquier prueba dada. Sin embargo, una disciplina TDD puede, muy fácilmente, crear una clase que contenga un Diccionario con todos los valores especificados en las pruebas y sus enteros. Sucedió en nuestro dojo. Aquí está el problema; si los requisitos reales establecidos del analizador eran que manejaba solo los números que probamos, no hicimos nada malo; El sistema "funciona" y no perdimos el tiempo diseñando algo más detallado que funciona en el caso general. Sin embargo, los nuevos Agilites observaron el embrollo y dijeron que este enfoque era estúpido; Nosotros "sabíamos" que tenía que ser más inteligente y más robusto. ¿Pero nosotros? Este es el poder de TDD y su debilidad; no puede diseñar nada más o menos que un sistema que cumpla con los requisitos establecidos por el usuario, ya que no debe (y con frecuencia no puede) escribir un código que no cumpla o probar algún requisito del sistema que le entregó la persona que paga el facturas

Aunque hago un poco de escritura de prueba posterior al desarrollo, hay un gran problema al hacerlo; ya ha escrito el código de producción, y es de esperar que lo haya probado de alguna otra manera. Si falla tu prueba ahora, ¿quién está equivocado? Si es la prueba, entonces cambia la prueba para afirmar que lo que el programa está generando actualmente es correcto. Bueno, eso no es de mucha utilidad; acabas de probar que el sistema produce lo que siempre tiene. Si es el SUT, entonces tienes problemas más grandes; tienes un objeto que ya has desarrollado completamente que no pasa tu nueva prueba, y ahora tienes que rasgarlo y cambiarlo para que lo haga. Si esa es su única prueba automatizada de este objeto hasta la fecha, ¿quién sabe qué romperá para aprobar esta prueba? TDD, en cambio, lo obliga a escribir la prueba antes de incorporar cualquier nueva lógica que pase esa prueba y, como resultado, tiene un código a prueba de regresión; tiene un conjunto de pruebas que demuestran que el código cumple con los requisitos actuales, antes de comenzar a agregar nuevos. Por lo tanto, si las pruebas existentes fallan cuando se agrega el código, usted rompió algo y no debe confirmar ese código para una versión hasta que pase todas las pruebas que ya estaban allí Y todas las nuevas.

Si hay un conflicto en tus pruebas, eso es un bloqueo. Supongamos que tiene una prueba que prueba que un método dado devuelve X dado A, B y C. Ahora, tiene un nuevo requisito, y al desarrollar las pruebas descubre que ahora el mismo método debe mostrar Y cuando se le da A, B y C. Bueno, la prueba anterior es esencial para probar que el sistema funcionó a la antigua, por lo que cambiar esa prueba para demostrar que ahora devuelve Y puede romper otras pruebas basadas en ese comportamiento. Para resolver esto, debe aclarar que el nuevo requisito es un cambio en el comportamiento del anterior, o que uno de los comportamientos se inferió incorrectamente de los requisitos de aceptación.

Recientemente (en la última semana) me he embarcado en un experimento en el que intento codificar una nueva característica en un proyecto en el que estoy trabajando con los principios de TDD. En el pasado, nuestro enfoque ha sido moderadamente ágil, pero sin gran rigor. Las pruebas unitarias ocurren aquí y allá cuando es conveniente. La principal barrera para la cobertura de pruebas integrales es que nuestra aplicación tiene una complicada red de dependencias. Escogí una función que era conveniente tapar para probar mi experimento; Los detalles no son importantes y, probablemente, son sensibles a los aspectos comerciales, basta con decir que se trata de un simple problema de optimización.

Hasta aquí he encontrado que:

  • TDD, para mí, parece alentar a los diseños divagantes y no obvios a tomar forma. La restricción de que no se debe escribir código sin una prueba tiende a bloquear las oportunidades de factorizar la funcionalidad en unidades independientes. Preparar y escribir pruebas para tantas funciones a la vez es demasiado difícil en la práctica
  • TDD tiende a fomentar la creación de ''Objetos de Dios'' que lo hacen todo, porque ya has escrito muchas clases de burla para la clase x, pero pocas para la clase y, por lo que parece lógico en el momento en que la clase x también debe implementar la función z En lugar de dejarlo a clase y.
  • Escribir pruebas antes de escribir código requiere que tenga una comprensión completa de cada complejidad del problema antes de resolverlo. Esto parece una contradicción.
  • No he podido hacer que el equipo se ponga en marcha para comenzar a utilizar un marco de burla. Esto significa que hay una proliferación de cruft creada únicamente para probar una característica particular. Por cada método probado, tenderá a necesitar un falso cuyo único trabajo es informar que la clase bajo prueba llamó lo que se supone que debe hacer. Estoy empezando a encontrarme escribiendo algo parecido a un DSL simplemente para crear una instancia de los datos de prueba.
  • A pesar de las preocupaciones anteriores, TDD ha producido un diseño funcional con pocos errores misteriosos, a diferencia del patrón de desarrollo al que estoy acostumbrado. Refactorizar el extenso lío que resulta, sin embargo, ha requerido que abandone temporalmente el TDD y simplemente lo termine. Confío en que las pruebas continuarán aplicando la corrección en el método. Intentando con TDD, el ejercicio de refactorización creo que va a proliferar más.

La pregunta entonces es: "¿Alguien tiene algún consejo para reducir el impacto de las preocupaciones enumeradas anteriormente?". No tengo ninguna duda de que un marco burlón sería ventajoso; Sin embargo, en este momento ya estoy presionando a mi suerte al intentar algo que parece simplemente producir un código enmarañado.

editar # 1:

Gracias a todos por sus respuestas consideradas. Admito que escribí mi pregunta después de unas cuantas cervezas los viernes por la noche, por lo que en algunos lugares es vago y en realidad no expresa los sentimientos que realmente pretendía. Me gustaría enfatizar que me gusta la filosofía de TDD, y la he encontrado moderadamente exitosa, pero también sorprendente por las razones que mencioné. Tengo la oportunidad de dormir y ver el problema otra vez con nuevos ojos la próxima semana, así que tal vez pueda resolver mis problemas resolviéndome. Sin embargo, ninguno de ellos no es titular.

Lo que más me preocupa es que algunos de los miembros del equipo se resisten a probar cualquier cosa que pueda llamarse una ''técnica'' a favor de ''solo hacerlo''. Me preocupa que la aparición de cruft se tome como una marca negra en el proceso, en lugar de evidencia de que se debe hacer completamente (es decir, con un marco de burla y una DI fuerte) para obtener mejores resultados.

RE "TDD no tiene por qué significar prueba primero": ( womp, btreat )

La "regla de oro" en todos los textos que he encontrado sobre el tema es "Rojo, Verde, Refactor". Es decir:

  • Escribe una prueba que DEBE fallar
  • Escribir código que pase la prueba.
  • Refactorice el código para que pase la prueba de la manera más práctica posible.

Tengo curiosidad por saber cómo se puede imaginar hacer un desarrollo impulsado por pruebas sin seguir el principio básico de TDD como se escribió originalmente. Mi colega llama a la casa de rehabilitación (o un enfoque diferente e igualmente válido, según su perspectiva) "Desarrollo de validación de prueba". En este caso, creo que es útil acuñar un nuevo término, o posiblemente robarlo de otra persona y tomarle crédito por ello.

DSL RE para datos de prueba: ( Michael Venable )

Me alegra que hayas dicho eso. Veo que la forma general es cada vez más útil en el ámbito del proyecto, ya que la aplicación en cuestión mantiene un gráfico de objetos bastante complicado y, por lo general, probarlo significa ejecutar la aplicación y probar cosas en la GUI. (No voy a revelar el juego por razones de sensibilidad comercial arriba, pero básicamente tiene que ver con la optimización de varias métricas en un gráfico dirigido. Sin embargo, hay muchas advertencias y widgets configurables por el usuario involucrados).

Ser capaz de establecer un caso de prueba significativo programáticamente ayudará en todo tipo de situaciones, potencialmente no limitadas a pruebas de unidad.

RE Dios Objetos:

Me sentí así porque una clase parecía estar ocupando la mayor parte del conjunto de características. Tal vez esto esté bien, y realmente es tan importante, pero levantó algunas cejas porque parecía un código más antiguo que no se desarrolló en este sentido y parecía violar el SRP. Supongo que es inevitable que algunas clases funcionen principalmente como uniones entre numerosos bits diferentes de funcionalidad encapsulada y otras solo coserán unas pocas. Si va a ser de esa manera, supongo que lo que debo hacer es purgar la mayor lógica posible de este Objeto de Dios aparente y volver a redactar su comportamiento como un punto de unión entre todas las partes excluidas.

(a los moderadores: agregué mis respuestas a las publicaciones aquí porque el campo de comentarios no es lo suficientemente largo para contener los detalles que me gustaría).

Edición # 2 (después de unos cinco meses):

Bueno, sentí que sería bueno actualizar con algunos pensamientos más después de reflexionar sobre el problema por un tiempo.

Terminé abandonando el enfoque TDD al final, lamento decirlo. Sin embargo, siento que hay algunas razones específicas y justificadas para esto, y estoy listo para continuar con ello la próxima vez que tenga una oportunidad.

Una consecuencia de la mentalidad de refactorización no arrepentida de TDD es que no me molestó mucho cuando, al examinar brevemente mi código, el desarrollador principal declaró que la gran mayoría no tenía sentido y tenía que ir. Si bien hay una punzada de pesar por tener que deshacerse de una gran franja de trabajo duro, vi exactamente lo que quería decir.

Esta situación surgió porque tomé literalmente la regla de ''código para una interfaz'', pero seguí escribiendo clases que intentaban representar la realidad. Hace bastante tiempo hice la primera declaración:

Las clases no deben intentar representar la realidad. El modelo de objeto solo debe intentar resolver el problema en cuestión.

... que he repetido tantas veces como puedo desde entonces; para mí y para cualquier otra persona que escuche.

El resultado de este comportamiento fue un modelo de objeto de clases que realizaban una función y un conjunto de interfaces de duplicación que repetía la funcionalidad de las clases. Habiéndome señalado esto y luego de un breve pero intenso período de resistencia, vi la luz y no tuve ningún problema en eliminar la mayor parte.

Eso no quiere decir que creo que ''código para una interfaz'' es una tontería. Lo que significa es que la codificación de una interfaz es principalmente valiosa cuando las interfaces representan funciones comerciales reales, en lugar de las propiedades de un modelo de objeto perfecto imaginado que parece una copia en miniatura de la vida real, pero no tiene su único significado en Vida para estar respondiendo la pregunta que originalmente hiciste. La fortaleza de TDD es que no puede producir modelos como este, excepto por casualidad. Ya que comienza con hacer una pregunta y solo le importa obtener una respuesta, su ego y su conocimiento previo del sistema no están involucrados.

Estoy divagando ahora, así que haré todo lo posible para terminar esto y simplemente afirmar que tengo muchas ganas de intentar TDD nuevamente, pero tengo una mejor visión general de las herramientas y tácticas disponibles y haré todo lo posible para decidir cómo quiero hacerlo antes de saltar. Tal vez debería trasplantar este gofre a un blog al que pertenece, una vez que tenga algo más que decir al respecto.


Creo que está cayendo bajo la idea errónea de que TDD siempre significa "probar primero". El desarrollo de primera prueba no es necesariamente lo mismo que TDD. TDD es una metodología de ingeniería de software que se enfoca en escribir código verificable. No hay un requisito estricto de escribir siempre las pruebas primero para practicar TDD.

¡Déjame desglosar tus argumentos y espero poder ayudarte a aclarar algunos de tus bloqueos!

TDD for me seems to encourage rambling, non-obvious designs to take

forma. La restricción de que no se debe escribir código sin una prueba tiende a bloquear las oportunidades de factorizar la funcionalidad en unidades independientes. Preparar y escribir pruebas para tantas funciones a la vez es demasiado difícil en la práctica

TDD trata de escribir código verificable. El punto de escribir las pruebas primero es para que pueda hacer un esquema de lo que están haciendo sus clases y asegurarse de diseñar esas clases para que sean comprobables.

Lo que sus pruebas no deberían estar haciendo es bloquearlo o forzar su diseño. Las pruebas son mutables: si escribe una prueba y luego le impide refactorizar algo, simplemente elimine la prueba. No puede estar escribiendo su código de trabajo para tener en cuenta las pruebas que ya están escritas. Las pruebas están ahí para respaldar tu código, no al revés.

TDD tends to encourage the creation of ''God Objects'' that do

todo, porque ya ha escrito muchas clases de simulacros para la clase x, pero pocas para la clase y, por lo que parece lógico en el momento en que la clase x también debería implementar la característica z en lugar de dejarla en la clase y.

I haven''t been able to get the team on-side to start using a mocking

marco de referencia. Esto significa que hay una proliferación de cruft creada únicamente para probar una característica particular. Por cada método probado, tenderá a necesitar un falso cuyo único trabajo es informar que la clase bajo prueba llamó lo que se supone que debe hacer. Estoy empezando a encontrarme escribiendo algo parecido a un DSL simplemente para crear una instancia de los datos de prueba.

La burla debería hacerse con un marco para eliminar la carga de escribir todas estas clases de burla. De nuevo, parece que está tratando sus pruebas como estáticas y escribiendo su código real para adaptarse a las pruebas existentes. Este no es un problema de TDD al que se enfrenta, es un problema de administración. Es tu equipo el que está creando el cruft, no el proceso TDD. El solo hecho de mover una característica a una clase a la que no pertenece es una elección consciente por conveniencia, y no una ingeniería adecuada. Si la burla es la carga, entonces ese es el problema que está llevando a las opciones perezosas.

Writing tests before you write code requires that you have a complete

comprensión de cada complejidad del problema antes de resolverlo. Esto parece una contradicción.

Las pruebas deben evolucionar fluidamente con su código. No hay un requisito para entender todo antes de comenzar a escribir pruebas para él; se supone que las pruebas lo ayudarán a crear su diseño para que pueda crear clases buenas y comprobables. Si tiene que apagar y construir un código real por unos días y luego regresar y actualizar sus pruebas, la policía de TDD no va a arruinar su puerta. El punto es que cuando estás construyendo tus clases, estás pensando en cómo puedes probarlas. Escribir exámenes al mismo tiempo es genial, pero hay ocasiones en las que no sabrás lo suficiente como para escribirlas todavía. Las pruebas deben estar trabajando con usted, no contra usted.

Recuerde: "probar primero" es solo una forma de hacer TDD. No es la única manera, y de hecho, nunca he conocido a nadie que haya practicado "probar primero" para cada pieza de código que escriben.


Creo que lo que sientes es normal al comenzar con TDD. Su código debe escribirse siguiendo ciertas convenciones para admitir TDD (como inyección de dependencias) y cambiar su estilo de escritura de código para admitirlo sucede a través del hábito a lo largo del tiempo.

No me sorprende que te encuentres escribiendo un DSL para tus pruebas, y no creo que eso sea algo malo. Hace que la prueba sea más fácil de leer, y eso contribuirá en gran medida a ayudar a mantener las pruebas. Espero que sea más fácil para sus compañeros de trabajo agregar sus propias pruebas también. Últimamente, me encuentro usando una interfaz fluida para hacer que el código de prueba sea legible y para tener en cuenta la lógica común en un solo lugar.

Ejemplo:

LoadModelSetupFromTestFileCollection("VariableSetToScript.xml"); AssertVariable("variable").HasValue(3);

Probar todas las funciones de su código es mucho trabajo, pero no conozco otra manera de decir con confianza que sabe que todo su código funciona. Y tener un conjunto automatizado de pruebas realmente vale la pena a largo plazo, ya que futuros cambios de código rompen el código existente.

Las mejores sugerencias que te puedo dar:

  • Elige qué es una unidad. No tiene que ser una clase. Un pequeño módulo (con varias clases) puede servir como una unidad bajo prueba. Hacer esto reduce la necesidad de burlarse ya que prueba el grupo de clases a la vez. La única desventaja es que la depuración es más difícil, pero sus pruebas son igual de efectivas.

  • Me gusta escribir pruebas antes del código, pero a veces, realmente necesitas explorar ideas primero. Está bien hacer más escritura de código "exploratoria" cuando sea necesario y escribir las pruebas más adelante. Sin embargo, sospecho que la mayoría de las veces, tiene una buena idea de las funciones que necesitará antes de comenzar a escribir el código.

  • Factoriza tu código de prueba en funciones. He realizado cambios en el código que apenas impactaron en el código de producción, pero causaron efectos de ondulación a través del código de prueba. Los códigos de prueba bien calculados solucionan esto y ayudan al mantenimiento.

  • El código de prueba que se asemeja a un lenguaje similar a DSL tiene un largo camino de mantenimiento y facilita la creación de nuevas pruebas, IMO.

Creo que estás en el camino correcto. Si le cuesta trabajo convencer a la gente de que se burle, no lo requiera. Si sientes que probar cada función es demasiado, entonces prueba las funciones que tienen más probabilidades de fallar. Empieza pequeño. A medida que el equipo se sienta cómodo con esto, es de esperar que comiencen a crear más pruebas. En cualquier caso, tener algunas pruebas es mejor que ninguna prueba en absoluto.


Para empezar, intente revisar su método con un libro de referencia (por ejemplo, el libro de Beck). Cuando estés aprendiendo algo, sigue la regla sin preguntar. Un error común es adaptar un método prematuramente sin comprender las implicaciones de sus cambios.
por ejemplo (como Carl publicó) los libros que he leído defienden escribir una prueba de unidad a la vez y verla fallar antes de completar la implementación.

Una vez que pasa, necesitas "refactorizar". Palabra pequeña pero grandes implicaciones: es el paso de hacer o romper. Mejoras tu diseño en una ráfaga de pequeños pasos. Sin embargo, TDD no es un sustituto para la experiencia ... que se deriva de la práctica. Por lo tanto, un programador experimentado con / sin TDD aún puede terminar produciendo un código mejor que un principiante con TDD, porque él / ella sabe qué buscar. Entonces, ¿cómo llegar allí? Aprendes de las personas que lo han estado haciendo por un tiempo.

  • Recomendaría el libro TDD By Example de Beck primero. (El libro GOOS de Freeman y Pryce es bueno, pero obtendrías un mejor valor una vez que hayas estado haciendo TDD por un tiempo).
  • Para aprovechar la mente del Gurú, echa un vistazo a Clean Code de Bob Martin. Te da heurísticas simples para evaluar tus elecciones en contra. Estoy en el capítulo 3; Incluso he configurado un ejercicio de lectura grupal en el trabajo. Como dice el libro, el código limpio es igual medida de disciplina, técnica + "sentido del código".

Personalmente, no soy un fanático de las pruebas de escritura antes del código por los motivos que mencionó. En la práctica, prefiero seguir una metodología para escribir un código, escribir las pruebas para el código, asegurarme de que se ejecuten las pruebas y luego confirmar el código y las pruebas. Esto evita una serie de problemas que mencionó al tiempo que mantiene algunos de los beneficios que TDD pretende promover. El código está escrito de manera que facilite las pruebas que finalmente seguirán, pero el código impulsa las pruebas en lugar de viceversa.


Recomendaría mucho continuar con su enfoque por un tiempo, y luego leer el libro de Gerard Mezaros, xUnit Test Patterns, y tratar de aplicar sus pautas. TDD es un camino largo y sinuoso y toma bastante tiempo comenzar a ver los beneficios. Mis respuestas breves a algunas de sus preocupaciones son a continuación:

  • TDD alienta diseños no obvios. A menudo estos son mejores que los obvios. Algunos de los katas que puede encontrar en línea muestran esta característica, donde puede haber imaginado un conjunto completo de resultados de clases de TDD en una o dos con muy poco código. No estoy de acuerdo en que bloquee las oportunidades de factorizar los bits: el mantra de TDD es rojo, verde, refactor. Creo que el secreto aquí no es pensar en todas las funciones a la vez; mantén una a la vez desde un nivel alto antes de que empieces a pensar en cómo se logrará en las clases.
  • Ese podría ser el caso, pero luego, cuando tenga su sombrero de refactor, siempre podrá refactorizar su clase principal en múltiples clases, con la certeza de que sus pruebas detectarán cualquier error en su refactorización. TDD fomenta la refactorización en cada oportunidad (siempre que tenga luz verde) para que los objetos de Dios no sean el resultado.
  • No estoy de acuerdo con esto. Todo lo que requiere TDD es que usted sepa una cosa que necesita y escriba esa prueba. Entonces lo haces pasar. Entonces piensas en otra cosa que necesitas. Los varios katas ilustran esto bastante bien. El libro original de Kent Beck sobre TDD también ilustra este proceso.
  • La burla es difícil, esto es cierto. Además, un DSL para configurar datos de prueba es una gran idea :)
  • Por lo que sé, por definición, la refactorización no requiere más pruebas. Refactoriza bajo una barra verde, lo que significa que nunca escribe pruebas para su refactorización. Es posible que una refactorización conduzca a la creación de una nueva clase, momento en el que es posible que desee crear una nueva clase de prueba para probar su nueva clase y trasladar las pruebas una a la vez a la nueva clase de prueba, pero generalmente la refactorización se realiza sin la Además de nuevas pruebas.

TDD no se burla. A veces, la burla facilita el desarrollo de las pruebas, pero si en su primer paso en TDD está comenzando con las simulaciones, probablemente no esté recibiendo la mejor introducción posible a la práctica.

TDD no, en mi experiencia, conduce a objetos de dios; todo lo contrario. TDD me lleva a clases que hacen menos cosas e interactúan con menos clases, menos dependencias.

La restricción de que no se debe escribir código sin una prueba tiende a bloquear las oportunidades de factorizar la funcionalidad en unidades independientes. Preparar y escribir pruebas para tantas características simultáneamente es demasiado difícil en la práctica.

Esto realmente no me suena bien. No está intentando escribir pruebas para muchas características simultáneamente; está intentando escribir una prueba, para una característica, a la vez. Cuando se escribe esa prueba, la haces pasar. Cuando está pasando, lo haces limpio. Luego, escribe tu próxima prueba, posiblemente impulsando un mayor desarrollo de la misma función, hasta que la función esté terminada y limpia. Luego escribes la siguiente prueba para tu próxima característica.

Escribir pruebas antes de escribir código requiere que tenga una comprensión completa de cada complejidad del problema antes de resolverlo. Esto parece una contradicción.

De nuevo: escribe una prueba. Eso requiere una comprensión completa de un aspecto de una característica. Lo requiere, y lo expresa concretamente, en forma ejecutable.