¿Qué pierdo al adoptar un diseño basado en pruebas?
Enumere solo los negativos; no enumere los beneficios escritos en forma negativa.
¿Qué pierdo al adoptar un diseño basado en pruebas?
Enumere solo los negativos; no enumere los beneficios escritos en forma negativa.
Respuestas:
Varias desventajas (y no estoy afirmando que no haya beneficios, especialmente al escribir la base de un proyecto, ahorraría mucho tiempo al final):
Si desea hacer TDD "real" (lea: pruebe primero con los pasos rojo, verde y refactorizador), luego también debe comenzar a usar simulacros / trozos, cuando desee probar los puntos de integración.
Cuando comience a usar simulacros, después de un tiempo, querrá comenzar a usar la Inyección de dependencia (DI) y un contenedor de Inversión de control (IoC). Para hacer eso, necesita usar interfaces para todo (que tienen muchos escollos).
Al final del día, tienes que escribir mucho más código, que si lo hicieras "a la antigua usanza". En lugar de solo una clase de cliente, también necesita escribir una interfaz, una clase simulada, alguna configuración de IoC y algunas pruebas.
Y recuerde que el código de prueba también debe mantenerse y cuidarse. Las pruebas deben ser tan legibles como todo lo demás y lleva tiempo escribir un buen código.
Muchos desarrolladores no entienden cómo hacer todo esto "de la manera correcta". Pero debido a que todos les dicen que TDD es la única forma verdadera de desarrollar software, simplemente intentan lo mejor que pueden.
Es mucho más difícil de lo que uno podría pensar. A menudo, los proyectos realizados con TDD terminan con una gran cantidad de código que nadie entiende realmente. Las pruebas unitarias a menudo prueban la cosa incorrecta, la manera incorrecta. Y nadie está de acuerdo en cómo debería ser una buena prueba, ni siquiera los llamados gurús.
Todas esas pruebas hacen que sea mucho más difícil "cambiar" (opuesto a la refactorización) el comportamiento de su sistema y los cambios simples simplemente se vuelven demasiado difíciles y requieren mucho tiempo.
Si lee la literatura de TDD, siempre hay algunos ejemplos muy buenos, pero a menudo en las aplicaciones de la vida real, debe tener una interfaz de usuario y una base de datos. Aquí es donde TDD se pone realmente difícil, y la mayoría de las fuentes no ofrecen buenas respuestas. Y si lo hacen, siempre implica más abstracciones: simulacros de objetos, programación a una interfaz, patrones MVC / MVP, etc., que nuevamente requieren mucho conocimiento, y ... tienes que escribir aún más código.
Así que tenga cuidado ... si no tiene un equipo entusiasta y al menos un desarrollador experimentado que sepa cómo escribir buenas pruebas y también sabe algunas cosas sobre una buena arquitectura, realmente tiene que pensar dos veces antes de seguir el camino TDD .
Cuando llegue al punto en el que tiene una gran cantidad de pruebas, cambiar el sistema puede requerir reescribir algunas o todas sus pruebas, dependiendo de cuáles fueron invalidadas por los cambios. Esto podría convertir una modificación relativamente rápida en una muy lenta.
Además, puede comenzar a tomar decisiones de diseño basadas más en TDD que en principios de diseño realmente buenos. Si bien puede haber tenido una solución muy simple y fácil que es imposible de probar la forma en que TDD lo exige, ahora tiene un sistema mucho más complejo que en realidad es más propenso a errores.
if part of the system is covered by tests and they pass, then everything is fine (including design)
.
Creo que el mayor problema para mí es la enorme pérdida de tiempo que lleva "entrar". Todavía estoy muy al comienzo de mi viaje con TDD (vea mi blog para ver las actualizaciones de mis aventuras de prueba si está interesado) y literalmente he pasado horas comenzando.
Lleva mucho tiempo poner su cerebro en "modo de prueba" y escribir "código comprobable" es una habilidad en sí misma.
TBH, estoy respetuosamente en desacuerdo con los comentarios de Jason Cohen sobre hacer públicos los métodos privados, de eso no se trata. No he hecho más métodos públicos en mi nueva forma de trabajar que antes . Sin embargo, sí implica cambios arquitectónicos y le permite "conectar en caliente" módulos de código para hacer que todo lo demás sea más fácil de probar. Usted debe no estar haciendo la parte interna de su código sea más accesible para hacer esto. De lo contrario, volvemos al punto de partida con todo siendo público, ¿dónde está la encapsulación en eso?
Entonces, (OMI) en pocas palabras:
PD: Si desea enlaces a aspectos positivos, le he preguntado y respondido varias preguntas al respecto, consulte mi perfil .
En los pocos años que llevo practicando Test Driven Development, debo decir que los mayores inconvenientes son:
TDD se realiza mejor en parejas. Por un lado, es difícil resistir el impulso de simplemente escribir la implementación cuando SABES cómo escribir una declaración if / else . Pero un par te mantendrá en la tarea porque tú lo mantienes en la tarea. Lamentablemente, muchas compañías / gerentes no piensan que este sea un buen uso de los recursos. ¿Por qué pagar para que dos personas escriban una función, cuando tengo dos funciones que deben hacerse al mismo tiempo?
Algunas personas simplemente no tienen paciencia para escribir pruebas unitarias. Algunos están muy orgullosos de su trabajo. O bien, algunos al igual que ver métodos / funciones complicadas se desvanecen al final de la pantalla. TDD no es para todos, pero realmente desearía que lo fuera. Haría mucho más fácil mantener las cosas para esas pobres almas que heredan el código.
Idealmente, sus pruebas solo se romperán cuando tome una mala decisión de código. Es decir, usted pensó que el sistema funcionaba de una manera, y resulta que no lo hizo. Al romper una prueba o un conjunto (pequeño) de pruebas, esta es realmente una buena noticia. Sabe exactamente cómo afectará su nuevo código al sistema. Sin embargo, si sus pruebas están mal escritas, bien acopladas o, peor aún, generadas ( prueba de tos VS), entonces mantener sus pruebas puede convertirse en un coro rápidamente. Y, después de que suficientes pruebas comienzan a causar más trabajo que el valor percibido que están creando, entonces las pruebas serán lo primero que se eliminará cuando los horarios se compriman (por ejemplo, llega el momento de la crisis)
Idealmente, de nuevo, si se adhiere a la metodología, su código se probará al 100% de forma predeterminada. Por lo general, pensé, termino con una cobertura de código de más del 90%. Esto generalmente ocurre cuando tengo una arquitectura de estilo de plantilla, y la base se prueba, y trato de cortar esquinas y no probar las personalizaciones de la plantilla. Además, descubrí que cuando encuentro una nueva barrera que no había encontrado anteriormente, tengo una curva de aprendizaje al probarla. Admitiré escribir algunas líneas de código a la antigua manera de skool, pero realmente me gusta tener ese 100%. (Supongo que fui muy exitoso en la escuela, er skool).
Sin embargo, con eso diría que los beneficios de TDD superan con creces los negativos de la simple idea de que si puede lograr un buen conjunto de pruebas que cubran su aplicación pero no sean tan frágiles que un cambio las rompa a todas, lo hará podrá seguir agregando nuevas funciones en el día 300 de su proyecto como lo hizo en el día 1. Esto no sucede con todos los que prueban TDD pensando que es una bala mágica para todo su código lleno de errores, y por eso piensan que puede No funciona, punto.
Personalmente, descubrí que con TDD, escribo un código más simple, paso menos tiempo debatiendo si una solución de código en particular funcionará o no, y que no tengo miedo de cambiar ninguna línea de código que no cumpla con los criterios establecidos por el equipo.
TDD es una disciplina difícil de dominar, y he estado en esto durante algunos años, y todavía aprendo nuevas técnicas de prueba todo el tiempo. Es una inversión de tiempo enorme por adelantado, pero, a largo plazo, su sostenibilidad será mucho mayor que si no tuviera pruebas unitarias automatizadas. Ahora, si solo mis jefes pudieran resolver esto.
En su primer proyecto TDD hay dos grandes pérdidas, tiempo y libertad personal.
Pierdes tiempo porque:
Pierdes la libertad personal porque:
Espero que esto ayude
TDD requiere que planifiques cómo funcionarán tus clases antes de escribir el código para aprobar esas pruebas. Esto es tanto un plus como un menos.
Me resulta difícil escribir pruebas en un "vacío", antes de que se haya escrito ningún código. En mi experiencia, tiendo a tropezar con mis exámenes cada vez que inevitablemente pienso en algo mientras escribo mis clases que olvidé al escribir mis exámenes iniciales. Entonces es hora de refactorizar no solo mis clases, sino TAMBIÉN mis exámenes. Repita esto tres o cuatro veces y puede ser frustrante.
Prefiero escribir un borrador de mis clases primero y luego escribir (y mantener) una batería de pruebas unitarias. Después de tener un borrador, TDD funciona bien para mí. Por ejemplo, si se informa un error, escribiré una prueba para explotar ese error y luego corregiré el código para que pase la prueba.
La creación de prototipos puede ser muy difícil con TDD: cuando no está seguro de qué camino va a tomar para una solución, escribir las pruebas por adelantado puede ser difícil (aparte de las muy amplias). Esto puede ser un dolor.
Honestamente, no creo que para el "desarrollo central" de la gran mayoría de los proyectos haya ningún inconveniente real; se habla mucho más de lo que debería ser, generalmente por personas que creen que su código es lo suficientemente bueno como para no necesitar pruebas (nunca lo es) y personas que simplemente no pueden molestarse en escribirlas.
Bueno, y este estiramiento, necesita depurar sus pruebas. Además, hay un cierto costo de tiempo para escribir las pruebas, aunque la mayoría de las personas está de acuerdo en que es una inversión inicial que vale la pena durante la vida útil de la aplicación, tanto en depuración ahorrada como en estabilidad.
Sin embargo, el mayor problema que tuve personalmente con él es levantar la disciplina para escribir las pruebas. En un equipo, especialmente en un equipo establecido, puede ser difícil convencerlos de que el tiempo dedicado vale la pena.
Si sus pruebas no son muy exhaustivas, puede caer en una falsa sensación de "todo funciona" simplemente porque pasa las pruebas. Teóricamente, si sus pruebas pasan, el código está funcionando; pero si pudiéramos escribir código perfectamente la primera vez, no necesitaríamos pruebas. La moraleja aquí es asegurarse de hacer una verificación de cordura por su cuenta antes de llamar a algo completo, no solo confiar en las pruebas.
En esa nota, si su comprobación de cordura encuentra algo que no se ha probado, asegúrese de volver y escribir una prueba para ello.
La desventaja de TDD es que generalmente está estrechamente asociado con la metodología 'Agile', que no da importancia a la documentación de un sistema, sino que la comprensión detrás de por qué una prueba 'debería' devolver un valor específico en lugar de cualquier otro reside solo en el desarrollador cabeza.
Tan pronto como el desarrollador se vaya o se olvide de la razón por la que la prueba devuelve un valor específico y no otro, estás jodido. TDD está bien SI está adecuadamente documentado y rodeado de documentación legible para humanos (es decir, gerente de cabello puntiagudo) a la que se puede hacer referencia en 5 años cuando el mundo cambia y su aplicación también lo necesita.
Cuando hablo de documentación, esto no es una propaganda en el código, se trata de una escritura oficial que existe fuera de la aplicación, como casos de uso e información de fondo a la que pueden referirse los gerentes, abogados y el pobre savia que tiene que actualizar su código en 2011
Me he encontrado con varias situaciones en las que TDD me vuelve loco. Por nombrar algunos:
Capacidad de mantenimiento del caso de prueba:
Si está en una gran empresa, hay muchas posibilidades de que no tenga que escribir los casos de prueba usted mismo o al menos la mayoría de ellos sean escritos por otra persona cuando ingrese a la empresa. Las características de una aplicación cambian de vez en cuando y si no tiene un sistema en funcionamiento, como HP Quality Center, para rastrearlas, se volverá loco de inmediato.
Esto también significa que les tomará a los nuevos miembros del equipo una buena cantidad de tiempo captar lo que está sucediendo con los casos de prueba. A su vez, esto se puede traducir en más dinero necesario.
Probar la complejidad de la automatización:
Si automatiza algunos o todos los casos de prueba en scripts de prueba ejecutables por máquina, deberá asegurarse de que estos scripts de prueba estén sincronizados con sus casos de prueba manuales correspondientes y en línea con los cambios de la aplicación.
Además, pasará tiempo para depurar los códigos que lo ayudan a detectar errores. En mi opinión, la mayoría de estos errores provienen del fracaso del equipo de prueba para reflejar los cambios de la aplicación en el script de prueba de automatización. Los cambios en la lógica de negocios, la GUI y otras cosas internas pueden hacer que sus scripts dejen de ejecutarse o se ejecuten de manera poco confiable. A veces los cambios son muy sutiles y difíciles de detectar. Una vez que todas mis secuencias de comandos informan un error porque basan su cálculo en la información de la tabla 1, mientras que la tabla 1 ahora era la tabla 2 (porque alguien cambió el nombre de los objetos de la tabla en el código de la aplicación).
El mayor problema son las personas que no saben cómo escribir pruebas unitarias adecuadas. Escriben pruebas que dependen unas de otras (y funcionan muy bien con Ant, pero de repente fallan cuando las ejecuto desde Eclipse, solo porque se ejecutan en un orden diferente). Escriben pruebas que no prueban nada en particular: simplemente depuran el código, verifican el resultado y lo cambian a prueba, llamándolo "prueba1". Amplían el alcance de las clases y los métodos, solo porque será más fácil escribir pruebas unitarias para ellos. El código de las pruebas unitarias es terrible, con todos los problemas de programación clásicos (acoplamiento pesado, métodos que tienen 500 líneas de largo, valores codificados, duplicación de código) y es un infierno de mantener. Por alguna extraña razón, las personas tratan las pruebas unitarias como algo inferior al código "real", y no lo hacen No importa su calidad en absoluto. :-(
Pierdes mucho tiempo dedicado a escribir exámenes. Por supuesto, esto podría salvarse al final del proyecto al detectar errores más rápido.
El mayor inconveniente es que si realmente quieres hacer TDD correctamente, tendrás que fallar mucho antes de tener éxito. Dado el número de compañías de software que trabajan (dólares por KLOC), eventualmente lo despedirán. Incluso si su código es más rápido, más limpio, más fácil de mantener y tiene menos errores.
Si está trabajando en una empresa que le paga por los KLOC (o los requisitos implementados, incluso si no se han probado), manténgase alejado de TDD (o revisiones de código, programación de pares, integración continua, etc., etc.).
Pierde la capacidad de decir que está "listo" antes de probar todo su código.
Pierde la capacidad de escribir cientos o miles de líneas de código antes de ejecutarlo.
Pierde la oportunidad de aprender a través de la depuración.
Pierde la flexibilidad de enviar código del que no está seguro.
Pierdes la libertad de acoplar firmemente tus módulos.
Pierde la opción de omitir la escritura de documentación de diseño de bajo nivel.
Pierdes la estabilidad que viene con el código que todos temen cambiar.
Secundo la respuesta sobre el tiempo de desarrollo inicial. También pierde la capacidad de trabajar cómodamente sin la seguridad de las pruebas. También me han descrito como un loco de TDD, por lo que podrías perder algunos amigos;)
Reenfocarse en requisitos difíciles e imprevistos es la ruina constante del programador. El desarrollo basado en pruebas lo obliga a centrarse en los requisitos mundanos ya conocidos y limita su desarrollo a lo que ya se ha imaginado.
Piénselo, es probable que termine diseñando para casos de prueba específicos, por lo que no será creativo y comenzará a pensar "sería genial si el usuario pudiera hacer X, Y y Z". Por lo tanto, cuando ese usuario comienza a entusiasmarse con los posibles requisitos geniales X, Y y Z, su diseño puede estar demasiado rígidamente enfocado en casos de prueba ya especificados, y será difícil de ajustar.
Esto, por supuesto, es una espada de doble filo. Si pasa todo su tiempo diseñando para cada X imaginable, imaginable, X, Y y Z que un usuario pueda desear, inevitablemente nunca completará nada. Si completa algo, será imposible que alguien (incluido usted mismo) tenga alguna idea de lo que está haciendo en su código / diseño.
Puede ser difícil y lento escribir pruebas para datos "aleatorios" como fuentes XML y bases de datos (no es tan difícil). Últimamente he pasado algún tiempo trabajando con datos de clima. Es bastante confuso escribir pruebas para eso, al menos porque no tengo mucha experiencia con TDD.
Perderá grandes clases con múltiples responsabilidades. También es probable que pierda grandes métodos con múltiples responsabilidades. Puede perder algo de capacidad para refactorizar, pero también perderá parte de la necesidad de refactorizar.
Jason Cohen dijo algo como: TDD requiere una determinada organización para su código. Esto podría ser arquitectónicamente incorrecto; por ejemplo, dado que los métodos privados no se pueden invocar fuera de una clase, debe hacer que los métodos no sean privados para que sean verificables.
Digo que esto indica una abstracción perdida: si el código privado realmente necesita ser probado, probablemente debería estar en una clase separada.
Dave Mann
Debe escribir las aplicaciones de una manera diferente: una que las haga comprobables. Te sorprendería lo difícil que es esto al principio.
Algunas personas encuentran el concepto de pensar en lo que van a escribir antes de escribirlo demasiado duro. Conceptos como burlarse pueden ser difíciles para algunos también. TDD en aplicaciones heredadas puede ser muy difícil si no fueron diseñadas para pruebas. TDD en torno a marcos que no son compatibles con TDD también puede ser una lucha.
TDD es una habilidad por lo que los desarrolladores junior pueden tener dificultades al principio (principalmente porque no se les ha enseñado a trabajar de esta manera).
En general, los inconvenientes se resuelven a medida que las personas se vuelven hábiles y terminas abstrayendo el código 'maloliente' y tienes un sistema más estable.
Toma un tiempo entrar en él y algo de tiempo para comenzar a hacerlo en un proyecto, pero ... Siempre lamento no haber hecho un enfoque de Test Driven cuando encuentro errores tontos que una prueba automatizada podría haber encontrado muy rápido. Además, TDD mejora la calidad del código.
Buenas respuestas a todos. Añadiría algunas formas de evitar el lado oscuro de TDD:
He escrito aplicaciones para hacer su propia prueba aleatoria. El problema con escribir pruebas específicas es que incluso si escribes muchas de ellas, solo cubren los casos que piensas. Los generadores de prueba aleatoria encuentran problemas en los que no pensó.
El concepto completo de muchas pruebas unitarias implica que tiene componentes que pueden entrar en estados no válidos, como estructuras de datos complejas. Si se mantiene alejado de las estructuras de datos complejas, hay mucho menos que probar.
En la medida en que su aplicación lo permita, tenga cuidado con el diseño que se basa en el orden adecuado de notificaciones, eventos y efectos secundarios. Esos pueden caerse o mezclarse fácilmente, por lo que necesitan muchas pruebas.
TDD requiere una determinada organización para su código. Esto puede ser ineficiente o difícil de leer. O incluso arquitectónicamente equivocado; por ejemplo, desdeprivate
métodos no se pueden invocar fuera de una clase, debe hacer que los métodos no sean privados para que sean verificables, lo cual es simplemente incorrecto.
Cuando el código cambia, también debe cambiar las pruebas. Con la refactorización, esto puede ser mucho trabajo extra.
Permítanme agregar que si aplica los principios BDD a un proyecto TDD, puede aliviar algunos de los principales inconvenientes enumerados aquí (confusión, malentendidos, etc.). Si no está familiarizado con BDD, debería leer la introducción de Dan North. Se le ocurrió el concepto en respuesta a algunos de los problemas que surgieron al aplicar TDD en el lugar de trabajo. La introducción de Dan a BDD se puede encontrar aquí .
Solo hago esta sugerencia porque BDD aborda algunos de estos aspectos negativos y actúa como una brecha. Deberá tener esto en cuenta al recopilar sus comentarios.
Debe asegurarse de que sus pruebas estén siempre actualizadas, el momento en que comienza a ignorar las luces rojas es el momento en que las pruebas dejan de tener sentido.
También debe asegurarse de que las pruebas sean exhaustivas, o en el momento en que aparezca un gran error, el tipo de administración sofocante que finalmente convenció para permitirle pasar tiempo escribiendo más código se quejará.
La persona que enseñó el desarrollo ágil de mi equipo no creía en la planificación, solo escribiste tanto para el requisito más pequeño.
Su lema era refactor, refactor, refactor. Llegué a comprender que refactor significaba "no planificar con anticipación".
El tiempo de desarrollo aumenta: cada método necesita pruebas, y si tiene una aplicación grande con dependencias, debe preparar y limpiar sus datos para las pruebas.