unit testing - software - Desventajas del desarrollo basado en pruebas?
tdd vs bdd (30)
¿Qué pierdo al adoptar un diseño basado en pruebas?
Lista solo los negativos; No enumere los beneficios escritos en forma negativa.
Buenas respuestas a todos. Me gustaría añadir algunas maneras de evitar el lado oscuro de TDD:
He escrito aplicaciones para hacer su propia autoprueba aleatoria. El problema con la escritura de pruebas específicas es que si usted escribe muchas de ellas, solo cubren los casos que piensa. Los generadores de pruebas aleatorias encuentran problemas que no se te ocurrieron.
Todo el concepto de lotes de pruebas unitarias implica que tiene componentes que pueden entrar en estados no válidos, como estructuras de datos complejas. Si se mantiene alejado de estructuras de datos complejas, hay mucho menos que probar.
En la medida en que su aplicación lo permita, sea tímido con respecto al diseño que se basa en el orden correcto de notificaciones, eventos y efectos secundarios. Esos pueden fácilmente caerse o revolverse, por lo que necesitan muchas pruebas.
Bueno, y este estiramiento, necesitas depurar tus pruebas. Además, hay un cierto costo a tiempo para escribir las pruebas, aunque la mayoría de las personas están de acuerdo en que es una inversión inicial que se amortiza durante la vida útil de la aplicación, tanto en la depuración como en la estabilidad.
Sin embargo, el mayor problema que he tenido personalmente con este problema es mejorar la disciplina para escribir las pruebas. En un equipo, especialmente un equipo establecido, puede ser difícil convencerlos de que el tiempo empleado vale la pena.
Creo que el mayor problema para mí es la enorme pérdida de tiempo que lleva "llegar a ella". 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 para comenzar.
Se necesita mucho tiempo para poner a tu cerebro en "modo de prueba" y escribir "código comprobable" es una habilidad en sí misma.
TBH, discrepo respetuosamente con los comentarios de Jason Cohen sobre cómo hacer públicos los métodos privados, no se trata de eso. No he hecho más métodos públicos en mi nueva forma de trabajar que antes . Sin embargo, implica cambios en la arquitectura y le permite "conectar en caliente" módulos de código para que todo lo demás sea más fácil de probar. No debe hacer que los elementos internos de su código sean más accesibles para hacer esto. De lo contrario, volvemos a la cuadratura con todo siendo público, ¿dónde está la encapsulación en eso?
Entonces, (IMO) en pocas palabras:
- La cantidad de tiempo que se tarda en pensar (es decir, en realidad las pruebas de grok''ing).
- El nuevo conocimiento requerido de saber escribir código verificable.
- Comprender los cambios arquitectónicos necesarios para hacer que el código sea verificable.
- Aumentar tu habilidad de "TDD-Coder" mientras intentas mejorar todas las otras habilidades requeridas para nuestro glorioso oficio de programación :)
- Organice su base de código para incluir el código de prueba sin dañar su código de producción.
PD: Si desea enlaces a positivos, le he preguntado y respondido varias preguntas, revise mi perfil .
Cuando llega al punto en el que tiene una gran cantidad de pruebas, cambiar el sistema puede requerir volver a escribir algunas o todas sus pruebas, dependiendo de cuáles hayan sido 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 los buenos directores de diseño. Si bien es posible que haya tenido una solución muy simple y fácil que no se puede probar de la forma en que lo demanda TDD, ahora tiene un sistema mucho más complejo que en realidad es más propenso a cometer errores.
Debe asegurarse de que sus pruebas estén siempre actualizadas, en el momento en que comience a ignorar las luces rojas es el momento en que las pruebas pierden su significado.
También tiene que asegurarse de que las pruebas sean exhaustivas, o en el momento en que aparece un gran error, el tipo de administración sofocada que finalmente convenció para permitirle dedicar tiempo a escribir más código se quejará.
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 empresas de software que funcionan (en dólares por KLOC), eventualmente será despedido. 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 están probados), manténgase alejado de TDD (o revisiones de código, programación de pares, integración continua, etc., etc.).
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, simplemente porque se ejecutan en un orden diferente). Escriben pruebas que no prueban nada en particular; solo depuran el código, verifican el resultado y lo convierten en 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 pruebas unitarias es terrible, con todos los problemas de programación clásicos (acoplamiento pesado, métodos de 500 líneas de longitud, valores codificados, duplicación de códigos) y es un infierno de mantener. Por alguna extraña razón, las personas consideran las pruebas de unidad como algo inferior al código "real", y no les importa su calidad en absoluto. :-(
El tiempo de desarrollo aumenta: cada método necesita pruebas, y si tiene una aplicación grande con dependencias, necesita preparar y limpiar sus datos para las pruebas.
En los pocos años que llevo practicando Test Driven Development, debo decir que los mayores inconvenientes son:
Venderlo a la dirección.
TDD se hace mejor en pares. Por un lado, es difícil resistir la tentación de solo 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 empresas / gerentes no creen 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 se deben hacer al mismo tiempo?
Venderlo a otros desarrolladores.
Algunas personas simplemente no tienen la paciencia para escribir pruebas unitarias. Algunos están muy orgullosos de su trabajo. O, a algunos les gusta ver métodos / funciones complicadas desangrándose en el final de la pantalla. TDD no es para todos, pero realmente lo deseo. Haría mucho más fácil mantener las cosas para aquellas almas pobres que heredan el código.
Mantener el código de prueba junto con su código de producción
Idealmente, sus pruebas solo se interrumpirán cuando tome una decisión errónea sobre el código. Es decir, pensaste que el sistema funcionaba de una manera, y resultó que no. Al romper una prueba, o un (pequeño) conjunto de pruebas, esto es realmente una buena noticia. Usted sabe exactamente cómo su nuevo código afectará al sistema. Sin embargo, si sus pruebas están mal escritas, estrechamente relacionadas o, peor aún, generadas ( tos VS Test), el mantenimiento de sus pruebas puede convertirse rápidamente en un coro. Y, después de que suficientes pruebas comiencen a generar más trabajo que el valor percibido que están creando, entonces las pruebas serán lo primero que se eliminará cuando los programas se compriman (por ejemplo, llega al momento crítico)
Escribiendo pruebas para que cubras todo (100% de cobertura de código)
De manera ideal, nuevamente, si se adhiere a la metodología, su código se probará al 100% de manera predeterminada. Normalmente, pensé, termino con una cobertura de código de más del 90%. Esto suele suceder cuando tengo alguna arquitectura de estilo de plantilla, y la base está probada, y trato de cortar esquinas y no probar las personalizaciones de la plantilla. Además, he encontrado que cuando me encuentro con una nueva barrera que no había encontrado anteriormente, tengo una curva de aprendizaje para probarla. Admito que escribí algunas líneas de código a la manera antigua de Skool, pero realmente me gusta tener ese 100%. (Supongo que era un gran triunfador en la escuela, er skool).
Sin embargo, con eso diría que los beneficios de TDD superan con creces los aspectos 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 todas, podrá seguir agregando nuevas funciones el día 300 de su proyecto como lo hizo 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 cargado de errores, por lo que creen que puede No trabajes, punto.
Personalmente, descubrí que con TDD, escribo un código más simple, dedico menos tiempo a debatir si una solución de código en particular funcionará o no, y no temo 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, he estado trabajando durante algunos años y todavía aprendo nuevas técnicas de prueba todo el tiempo. Es una gran inversión de tiempo por adelantado, pero, a largo plazo, su sostenibilidad será mucho mayor que si no tuviera pruebas de unidad 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:
- La creación de un conjunto completo, refactorizado y mantenible de pruebas unitarias y de aceptación agrega mayor tiempo a la primera iteración del proyecto. Este puede ser un ahorro de tiempo a largo plazo, pero igualmente puede ser un tiempo que no tiene que perder.
- Debe elegir y convertirse en experto en un conjunto de herramientas básicas. Una herramienta de prueba de unidad debe complementarse con algún tipo de marco de burla y ambos deben formar parte de su sistema de compilación automatizado. También desea seleccionar y generar métricas adecuadas.
Pierdes la libertad personal porque:
- TDD es una forma muy disciplinada de escribir código que tiende a rozar contra los que están en la parte superior e inferior de la escala de habilidades. Escribir siempre el código de producción de una manera determinada y someter su trabajo a una revisión por pares continua puede asustar a sus peores y mejores desarrolladores e incluso llevar a la pérdida de personal.
- La mayoría de los métodos ágiles que incorporan TDD requieren que hables con el cliente continuamente sobre lo que te propones lograr (en esta historia / día / lo que sea) y cuáles son las compensaciones. Una vez más, esto no es una taza de té para todos, tanto para los desarrolladores como para los clientes.
Espero que esto ayude
He encontrado varias situaciones donde TDD me vuelve loco. Para nombrar algunos:
Mantenimiento de 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 que al menos la mayoría de ellos estén 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 implementado, como el Centro de calidad HP, para rastrearlas, se volverá loco en poco tiempo.
Esto también significa que los nuevos miembros del equipo tardarán bastante tiempo en captar lo que está pasando con los casos de prueba. A su vez, esto puede traducirse en más dinero necesario.
Automatización de pruebas de complejidad:
Si automatiza algunos o todos los casos de prueba en scripts de prueba ejecutables por máquina, tendrá que 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, dedicará tiempo a depurar los códigos que lo ayudan a detectar errores. En mi opinión, la mayoría de estos errores provienen de la falla del equipo de pruebas para reflejar los cambios de la aplicación en el script de prueba de automatización. Los cambios en la lógica empresarial, la GUI y otras cosas internas pueden hacer que sus scripts dejen de ejecutarse o ejecutarse de forma poco fiable. A veces los cambios son muy sutiles y difíciles de detectar. Una vez que todos mis scripts informaron un error porque basaron 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).
La creación de prototipos puede ser muy difícil con TDD: cuando no está seguro de qué camino tomará para encontrar 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" para la gran mayoría de los proyectos haya un verdadero inconveniente, sin embargo; se ha criticado 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.
La desventaja de TDD es que generalmente está estrechamente asociada con la metodología ''Agile'', que no le da importancia a la documentación de un sistema, sino a la comprensión de por qué una prueba ''debería'' devolver un valor específico en lugar de otro que reside solo en el desarrollador. cabeza.
Tan pronto como el desarrollador abandona u olvida la razón por la que la prueba devuelve un valor específico y no otro, estás equivocado. TDD está bien SI está documentado adecuadamente y está rodeado de documentación legible por humanos (es decir, un gerente de pelo puntiagudo) que se puede consultar en 5 años cuando el mundo cambie y su aplicación también lo necesite.
Cuando hablo de documentación, esto no es una propaganda en el código, se trata de un escrito oficial que existe de manera externa a la aplicación, como casos de uso e información de antecedentes a los que pueden referirse los gerentes, abogados y los pobres que tienen que actualizar Su código en 2011.
La persona que enseñó a mi equipo el desarrollo ágil no creía en la planificación, usted solo escribió tanto para el requisito más pequeño.
Su lema era refactor, refactor, refactor. Llegué a entender que refactor significaba "no planear con anticipación".
Perderás clases grandes con múltiples responsabilidades. También es probable que pierda grandes métodos con múltiples responsabilidades. Puede perder cierta capacidad para refactorizar, pero también perderá parte de la necesidad de refactorizar.
Jason Cohen dijo algo como: TDD requiere una cierta organización para su código. Esto podría ser arquitectónicamente incorrecto; por ejemplo, dado que los métodos privados no pueden llamarse 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
Permítame agregar que si aplica los principios de BDD a un proyecto de TDD, puede aliviar algunos de los principales inconvenientes que se enumeran aquí (confusión, malentendidos, etc.). Si no está familiarizado con la BDD, debería leer la introducción de Dan North. Se le ocurrió el concepto en respuesta a algunos de los problemas que surgieron de la aplicación de TDD en el lugar de trabajo. La introducción de Dan a BDD se puede encontrar here .
Solo hago esta sugerencia porque BDD aborda algunos de estos aspectos negativos y actúa como un punto muerto. Usted querrá considerar esto cuando recoja sus comentarios.
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.
Pierdes la oportunidad de aprender a través de la depuración.
Pierde la flexibilidad para enviar el código del que no está seguro.
Pierdes la libertad de acoplar firmemente tus módulos.
Se 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.
Se pierde el título de "hacker".
Puede ser difícil y llevar mucho tiempo escribir pruebas para datos "aleatorios" como XML-feeds y bases de datos (no es tan difícil). He pasado algún tiempo últimamente trabajando con feeds de datos meteorológicos. Es bastante confuso escribir pruebas para eso, al menos porque no tengo demasiada experiencia con TDD.
Reenfocarse en los requisitos difíciles e imprevistos es la pesadilla constante del programador. El desarrollo impulsado por 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 se volverá creativo y comenzará a pensar que "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 de enfriamiento X, Y y Z, su diseño puede estar demasiado 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 el tiempo diseñando para cada X, Y y Z concebible e imaginable que un usuario pueda desear, inevitablemente nunca completará nada. Si completas algo, será imposible que alguien (incluyéndote a ti mismo) tenga alguna idea de lo que estás haciendo en tu código / diseño.
Se necesita algo de tiempo para comenzar y algo más para comenzar a hacerlo en un proyecto, pero ... Siempre lamento no haber realizado un enfoque de prueba cuando encuentro errores tontos que una prueba automatizada podría haber encontrado muy rápido. Además, TDD mejora la calidad del código.
Se percibe como más lento. A largo plazo, eso no es cierto en términos de la pena que le ahorrará en el futuro, pero terminará escribiendo más código, así que podría decirse que está dedicando tiempo a "probar y no a codificar". Es un argumento defectuoso, pero lo preguntaste!
Se pierde mucho tiempo dedicado a escribir pruebas. Por supuesto, esto podría salvarse al final del proyecto al detectar errores más rápido.
Segundo 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 manillar TDD, por lo que podrías perder algunos amigos;)
Si desea hacer un TDD "real" (lea: pruebe primero con los pasos de refactor rojo, verde), entonces también debe comenzar a usar simulacros / apéndices cuando desee probar los puntos de integración.
Cuando empiece a usar simulacros, después de un tiempo, deseará comenzar a usar la inyección de dependencia (DI) y un contenedor de Inversión de control (IOC). Para hacer eso necesitas usar interfaces para todo (que tienen muchos escollos).
Al final del día, tienes que escribir mucho más código, que si solo lo haces a la "manera antigua". 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 se debe mantener y cuidar. Las pruebas deben ser tan legibles como todo lo demás y se necesita tiempo para escribir un buen código.
Muchos desarrolladores no entienden muy bien cómo hacer todo esto "de la manera correcta". Pero como todo el mundo les dice 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 estas pruebas hacen que sea mucho más difícil "cambiar" (al contrario de refactorizar) el comportamiento de su sistema y los cambios simples se vuelven demasiado difíciles y consumen mucho tiempo.
Si lees la literatura de TDD, siempre hay algunos ejemplos muy buenos, pero a menudo en las aplicaciones de la vida real, debes 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: objetos simulados, 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 escribir buenas pruebas y también sepa algunas cosas sobre la buena arquitectura, realmente tiene que pensárselo dos veces antes de ir por el camino de TDD .
Si sus pruebas no son muy exhaustivas, podría caer en una falsa sensación de que "todo funciona" simplemente porque las pruebas pasan. 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 revisión de la cordura por su cuenta antes de llamar a algo completo, no solo confíe 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.
TDD requiere que planifiques cómo funcionarán tus clases antes de escribir 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 no solo refactorizar mis clases, sino TAMBIÉN mis pruebas. Repita esto tres o cuatro veces y puede ser frustrante.
Prefiero escribir un borrador de mis clases primero, 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 de un error, escribiré una prueba para explotar ese error y luego corregiré el código para que la prueba pase.
TDD requiere una cierta organización para su código. Esto puede ser ineficiente o difícil de leer. O incluso arquitectónicamente incorrecto; por ejemplo, dado que private
métodos private
no pueden llamarse fuera de una clase, tiene que hacer que los métodos no sean privados para que sean verificables, lo cual es incorrecto.
Cuando el código cambia, también tienes que cambiar las pruebas. Con la refactorización esto puede ser una gran cantidad de trabajo extra.
Tienes que escribir las aplicaciones de una manera diferente: una que las haga verificables. 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. 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 que los desarrolladores junior pueden tener problemas al principio (principalmente porque no se les ha enseñado a trabajar de esta manera).
En general, aunque las desventajas 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.
Varias desventajas (y no estoy diciendo que no haya beneficios, especialmente al escribir los cimientos de un proyecto, le ahorraría mucho tiempo al final):
- Gran inversión de tiempo. Para el caso simple, pierde aproximadamente el 20% de la implementación real, pero para casos complicados pierde mucho más.
- Complejidad adicional. Para los casos complejos, sus casos de prueba son más difíciles de calcular, sugiero en casos como este, que intenten usar un código de referencia automático que se ejecutará en paralelo en la versión de depuración / prueba, en lugar de la prueba unitaria de los casos más simples.
- Impactos de diseño. A veces, el diseño no está claro al principio y evoluciona a medida que avanza, lo que obligará a rehacer su prueba, lo que generará una gran pérdida de tiempo. Yo sugeriría posponer las pruebas unitarias en este caso hasta que tenga una idea del diseño en mente.
- Ajustes continuos. Para las estructuras de datos y los algoritmos de caja negra, las pruebas unitarias serían perfectas, pero para los algoritmos que tienden a ser modificados, modificados o ajustados, esto puede causar una gran inversión de tiempo que se podría afirmar que no está justificado. Por lo tanto, úselo cuando crea que realmente se ajusta al sistema y no obligue al diseño a ajustarse a TDD.
- Las pruebas unitarias son más código para escribir, por lo tanto, un mayor costo inicial de desarrollo
- es mas codigo mantener
- aprendizaje adicional requerido