it-swarm-es.com

Diferencia de tiempo entre el desarrollo con pruebas unitarias y sin pruebas

Soy un desarrollador en solitario con un entorno de trabajo bastante limitado en tiempo en el que el tiempo de desarrollo generalmente varía de 1 a 4 semanas por proyecto, dependiendo de los requisitos, la urgencia o ambos. En cualquier momento manejo alrededor de 3-4 proyectos, algunos con líneas de tiempo que se superponen entre sí.

Como era de esperar, la calidad del código sufre. Tampoco tengo pruebas formales; Por lo general, pasa a caminar por el sistema hasta que se rompe un poco. Como resultado, una cantidad considerable de errores escapan a la producción, que tengo que arreglar y, a su vez, retrasa mis otros proyectos.

Aquí es donde intervienen las pruebas unitarias. Cuando se hace correctamente, debe mantener los errores, y mucho menos los que escapan a la producción, al mínimo. Por otro lado, las pruebas de escritura pueden tomar una cantidad considerable de tiempo, lo que no suena bien con proyectos de tiempo limitado como el mío.

La pregunta es, ¿qué diferencia de tiempo escribiría un código probado sobre un código no probado, y cómo se escala esa diferencia de tiempo a medida que se amplía el alcance del proyecto?

135
Revenant

Cuanto más tarde realice la prueba, más le costará escribir pruebas.

Cuanto más dura un error, más costoso es arreglarlo.

La ley de rendimientos decrecientes garantiza que pueda probarse a sí mismo en el olvido tratando de asegurarse de que no haya errores.

Buda enseñó la sabiduría del camino del medio. Las pruebas son buenas. Existe algo demasiado bueno. La clave es saber cuándo estás fuera de balance.

Cada línea de código que escriba sin pruebas tendrá costos significativamente mayores para agregar pruebas más tarde que si hubiera escrito las pruebas antes de escribir el código.

Cada línea de código sin pruebas será significativamente más difícil de depurar o reescribir.

Cada examen que escriba llevará tiempo.

Cada error llevará tiempo para solucionarlo.

Los fieles le dirán que no escriba una sola línea de código sin primero escribir una prueba fallida. La prueba asegura que está obteniendo el comportamiento que espera. Le permite cambiar el código rápidamente sin preocuparse de afectar al resto del sistema, ya que la prueba demuestra que el comportamiento es el mismo.

Debe comparar todo eso con el hecho de que las pruebas no agregan características. El código de producción agrega características. Y las características son las que pagan las facturas.

Hablando pragmáticamente, agrego todas las pruebas que puedo hacer. Ignoro los comentarios a favor de ver las pruebas. Ni siquiera confío en el código para hacer lo que creo que hace. Confío en las pruebas. Pero se me conoce por lanzar ocasionalmente el granizo de María y tener suerte.

Sin embargo, muchos codificadores exitosos no hacen TDD. Eso no significa que no prueben. Simplemente no insisten obsesivamente en que cada línea de código tenga una prueba automatizada en su contra. Incluso el tío Bob admite que no prueba su interfaz de usuario. También insiste en que elimines toda la lógica de la interfaz de usuario.

Como metáfora del fútbol (eso es fútbol americano), TDD es un buen juego de tierra. Las pruebas manuales solo donde escribes un montón de código y esperas que funcionen son un juego de pasar. Puedes ser bueno en cualquiera de los dos. Tu carrera no va a llegar a los playoffs a menos que puedas hacer ambas cosas. No formará el superbowl hasta que aprenda cuándo elegir cada uno. Pero si necesita un empujón en una dirección particular: las llamadas de los funcionarios van en mi contra más a menudo cuando paso.

Si desea probar TDD, le recomiendo que practique antes de intentar hacerlo en el trabajo. TDD hecho a la mitad, a medias y a medias es una gran razón por la que algunos no lo respetan. Es como verter un vaso de agua en otro. Si no te comprometes y lo haces rápida y completamente, terminas goteando agua por toda la mesa.

148
candied_orange

Estoy de acuerdo con el resto de las respuestas, pero para responder la pregunta cuál es la diferencia horaria directamente.

Roy Osherove en su libro The Art of Unit Testing, Second Edition página 200 hizo un estudio de caso sobre la implementación de proyectos de tamaño similar con equipos similares (habilidades inteligentes) para dos clientes diferentes donde un equipo hizo pruebas mientras que el otro no.

Sus resultados fueron así:

Team progress and output measured with and without tests

Entonces, al final de un proyecto, obtienes menos tiempo y menos errores. Esto, por supuesto, depende de qué tan grande es un proyecto.

112
Aki K

Solo hay un estudio que sé que estudió esto en un "entorno del mundo real": Mejora de la calidad a través del desarrollo basado en pruebas: resultados y experiencias de cuatro equipos industriales . Es costoso hacer esto de una manera sensata, ya que básicamente significa que necesita desarrollar el mismo software dos veces (o idealmente incluso más a menudo) con equipos similares, y luego tirar todos menos uno.

Los resultados del estudio fueron un aumento en el tiempo de desarrollo entre el 15% y el 35% (que no se acerca a la cifra 2x que suelen citar los críticos de TDD) y una disminución en la densidad de defectos previos a la liberación del 40% al 90% (! ) Tenga en cuenta que todos los equipos no tenían experiencia previa con TDD, por lo que se podría suponer que el aumento en el tiempo puede atribuirse al menos parcialmente al aprendizaje y, por lo tanto, se reduciría aún más con el tiempo, pero el estudio no lo evaluó.

Tenga en cuenta que este estudio trata sobre TDD, y su pregunta es sobre pruebas unitarias, que son cosas muy diferentes, pero es lo más cercano que pude encontrar.

30
Jörg W Mittag

Bien hecho, el desarrollo con pruebas unitarias puede ser más rápido incluso sin tener en cuenta los beneficios de los errores adicionales que se detectan.

El hecho es que no soy un codificador lo suficientemente bueno como para que mi código funcione tan pronto como se compila. Cuando escribo/modifico código, tengo que ejecutar el código para asegurarme de que haga lo que pensé que hace. En un proyecto, esto tendía a terminar pareciendo:

  1. Modificar código
  2. Compilar la aplicación
  3. Ejecutar aplicación
  4. Inicie sesión en la aplicación
  5. Abre una ventana
  6. Seleccione un elemento de esa ventana para abrir otra ventana
  7. Establezca algunos controles en esa ventana y haga clic en un botón

Y, por supuesto, después de todo eso, usualmente tomaba algunos viajes de ida y vuelta para hacerlo bien.

Ahora, ¿qué pasa si estoy usando pruebas unitarias? Entonces el proceso se parece más a:

  1. Escribir una prueba
  2. Ejecute pruebas, asegúrese de que falla de la manera esperada
  3. Escribir código
  4. Ejecute las pruebas nuevamente, vea que pasa

Esto es más fácil y rápido que probar manualmente la aplicación. Todavía tengo que ejecutar manualmente la aplicación (por lo que no me veo tonto cuando entrego el trabajo que realmente no funciona en absoluto), pero en su mayor parte ya he resuelto los problemas, y solo estoy verificando en ese punto. En realidad, normalmente hago este ciclo aún más estricto mediante el uso de un programa que vuelve a ejecutar automáticamente mis pruebas cuando guardo.

Sin embargo, esto depende de trabajar en una base de código amigable para las pruebas. Muchos proyectos, incluso aquellos con muchas pruebas, dificultan las pruebas de escritura. Pero si trabaja en ello, puede tener una base de código que sea más fácil de probar mediante pruebas automatizadas que con pruebas manuales. Como beneficio adicional, puede mantener las pruebas automatizadas y seguir ejecutándolas para evitar regresiones.

24
Winston Ewert

A pesar de que ya hay muchas respuestas, son algo repetitivas y me gustaría tomar un rumbo diferente. Las pruebas unitarias son valiosas, si y solo si, aumentan ¡valor comercial. Las pruebas por el bien de las pruebas (pruebas triviales o tautológicas), o para alcanzar alguna métrica arbitraria (como la cobertura del código), es una programación de culto a la carga.

Las pruebas son costosas, no solo en el tiempo que lleva escribirlas, sino también en el mantenimiento. Deben mantenerse sincronizados con el código que prueban o no valen nada. Sin mencionar el costo de tiempo de ejecutarlos en cada cambio. Eso no es un factor decisivo (o una excusa para no hacer los realmente necesarios), pero debe tenerse en cuenta para el análisis de costo-beneficio.

Entonces, la pregunta que debe hacerse al decidir si (o de qué tipo) probar o no una función/método, pregúntese "¿qué valor de usuario final estoy creando/protegiendo con esta prueba?". Si no puede responder esa pregunta, ¡fuera de su cabeza, entonces esa prueba probablemente no valga la pena el costo de escribir/mantener. (o no comprende el dominio del problema, que es un problema waaaay mayor que la falta de pruebas).

http://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf

21
Jared Smith

Depende de la persona, así como de la complejidad y la forma del código con el que está trabajando.

Para mí, en la mayoría de los proyectos, escribir pruebas unitarias significa que hago el trabajo un 25% más rápido. Sí, incluso incluyendo el tiempo para escribir las pruebas.

Porque el hecho es que el software no es hecho cuando escribe el código. Se realiza cuando se lo envía al cliente y están contentos con él. Las pruebas unitarias son, con mucho, la forma más eficiente que conozco para detectar la mayoría de los errores, aislar la mayoría de los errores para la depuración y ganar confianza en que el código es bueno. Tienes que hacer esas cosas de todos modos, así que hazlas bien.

9
Telastyn

La pregunta es, ¿qué diferencia de tiempo escribiría un código probado sobre un código no probado, y cómo se escala esa diferencia de tiempo a medida que se amplía el alcance del proyecto?

El problema empeora a medida que aumenta la antigüedad del proyecto: porque cada vez que agrega una nueva funcionalidad y/o cada vez que refactoriza la implementación existente, debe volver a probar lo que se probó anteriormente para asegurarse de que todavía funciona. Por lo tanto, para un proyecto de larga duración (de varios años), es posible que necesite no solo probar la funcionalidad sino volver a probarla 100 veces y más. Por esta razón, podría beneficiarse de tener automatizado pruebas. Sin embargo, IMO es lo suficientemente bueno (o incluso mejor) si se trata de pruebas automatizadas del sistema, en lugar de pruebas unitarias automatizadas.

Un segundo problema es que los errores pueden ser más difíciles de encontrar y corregir si no se detectan temprano. Por ejemplo, si hay un error en el sistema y sé que funcionaba perfectamente antes de que realizara su último cambio, entonces concentraré mi atención en su último cambio para ver cómo podría haber introducido el error. Pero si no sé si el sistema estaba funcionando antes de que hiciera su último cambio (porque el sistema no se probó correctamente antes de su último cambio), entonces el error podría estar en cualquier lugar.

Lo anterior se aplica especialmente al código profundo, y menos al código superficial, p. agregando nuevas páginas web donde es poco probable que las nuevas páginas afecten a las existentes.

Como resultado, una cantidad considerable de errores escapan a la producción, que tengo que arreglar y, a su vez, retrasa mis otros proyectos.

En mi experiencia, eso sería inaceptable, por lo que estás haciendo la pregunta equivocada. En lugar de preguntar si las pruebas acelerarían el desarrollo, debería preguntarse qué haría que el desarrollo fuera más libre de errores.

Una mejor pregunta podría ser:

  • ¿Las pruebas unitarias son el tipo correcto de pruebas, que necesita para evitar la "cantidad considerable de errores" que ha estado produciendo?
  • ¿Hay otros mecanismos de control de calidad/mejora (aparte de las pruebas unitarias) para recomendar también o en su lugar?

El aprendizaje es un proceso de dos etapas: aprende a hacerlo lo suficientemente bien, luego aprende a hacerlo más rápidamente.

4
ChrisW

Ha habido una larga historia de la Junta de Programadores que promueve TDD y otras metodologías de prueba, no recordaré sus argumentos y estaré de acuerdo con ellos, pero aquí hay cosas adicionales a considerar que deberían matizar un poco:

  • Las pruebas no son igualmente convenientes y eficientes según el contexto. Desarrollo software web, dime si tienes un programa para probar toda la interfaz de usuario ... en este momento estoy programando macros de Excel, ¿realmente debería desarrollar un módulo de prueba en VBA?
  • Escribir y mantener el software de prueba es un trabajo real que cuenta a corto plazo (vale la pena a largo plazo). Escribir pruebas relevantes también es una experiencia para obtener
  • Trabajar en equipo y trabajar solo no tiene los mismos requisitos de prueba porque en el equipo debe validar, comprender y comunicar el código que no escribió.

Yo diría que la prueba es buena, pero asegúrese de hacer la prueba temprano y comprobar dónde está la ganancia.

3
Arthur Havlicek

Algunos aspectos a considerar, no mencionados en las otras respuestas.

  • Beneficio adicional/costo adicional depende de la experiencia con la escritura de pruebas unitarias
    • con mi primer proyecto de prueba unitaria, los costos adicionales se triplicaron porque tuve que aprender mucho y cometí muchos errores.
    • después de 10 años de experiencia con tdd, necesito un 25% más de tiempo de codificación para escribir las pruebas por adelantado.
  • con más tdd-moduls todavía hay necesidad de prueba de gui manual y prueba de integración
  • tdd solo funciona cuando se hace desde el principio.
    • la aplicación de tdd a un proyecto existente y desarrollado es costoso/difícil. Pero puede implementar pruebas de regresión en su lugar.
  • las pruebas automatizadas (pruebas unitarias y otro tipo de pruebas) requieren constantes de mantenimiento para que sigan funcionando.
    • haber creado la prueba mediante copiar y pegar puede hacer que el mantenimiento del código de prueba sea costoso.
    • con una experiencia creciente, el código de prueba se vuelve más modular y más fácil de mantener.
  • con una experiencia creciente, tendrá la sensación de que vale la pena crear pruebas automatizadas y cuándo no.
    • ejemplo, no hay un gran beneficio para unittest getters/setters/wrappers simples
    • no escribo pruebas automatizadas a través de la interfaz gráfica de usuario
    • me aseguro de que el businesslayer pueda ser probado

Resumen

Al comenzar con tdd, es difícil alcanzar el estado de "más beneficio que costo" siempre y cuando se encuentre en un "entorno de trabajo con limitaciones de tiempo", especialmente si hay "gerentes inteligentes" que le dicen que "se deshaga de lo costoso e inútil". probando cosas "

Nota: con "pruebas unitarias" me refiero a "probar módulos en forma aislada".

Nota: con "prueba de regresión" quiero decir

  • escribe un código que produzca algún texto de salida.
  • escriba un código de "prueba de regresión" que verifique que el resultado de la generación sigue siendo el mismo.
  • la prueba de regresión le permite saber cada vez que cambia el resultado (que podría estar bien o un indicador de un nuevo error)
  • la idea de "prueba de regresión" es similar a pruebas de aprobación
    • ... tomando una instantánea de los resultados y confirmando que no han cambiado.
3
k3b

Los programadores, como las personas que se ocupan de la mayoría de las tareas, subestiman el tiempo que realmente lleva completarlo. Con eso en mente, pasar 10 minutos para escribir una prueba puede considerarse como el tiempo que uno podría haber pasado escribiendo toneladas de código cuando, en realidad, habría pasado ese tiempo creando el mismo nombre de función y parámetros que hizo durante la prueba . Este es un escenario TDD.

No escribir exámenes, es muy parecido a tener una tarjeta de crédito; Tendemos a gastar más o escribir más código. Más código tiene más errores.

En lugar de decidir tener una cobertura total de código o ninguna, sugiero centrarse en la parte crítica y complicada de su aplicación y hacer pruebas allí. En una aplicación bancaria, ese podría ser el cálculo de intereses. Una herramienta de diagnóstico del motor puede tener protocolos de calibración complejos. Si ha estado trabajando en un proyecto, probablemente sepa qué es y dónde están los errores.

Comience despacio. Desarrolla un poco de fluidez antes de juzgar. Siempre puedes parar.

3
JeffO

Puedo relacionarme con su experiencia: nuestra base de código casi no tenía pruebas y era en su mayoría no comprobable. Literalmente llevó años desarrollar algo y corregir errores de producción tomó un tiempo precioso de las nuevas características.

Para una reescritura parcial, prometí escribir pruebas para todas las funciones principales. Al principio, me llevó mucho más tiempo y mi productividad sufrió notablemente, pero luego mi productividad fue mejor que nunca.

Parte de esa mejora fue que tenía menos errores de producción que a su vez conducían a menos interrupciones -> Tenía un mejor enfoque en un momento dado.

Además, la capacidad de probar y depurar el código de forma aislada realmente vale la pena: un conjunto de pruebas es muy superior a un sistema que no se puede depurar, excepto con la configuración manual, e. sol. iniciar su aplicación y navegar a la pantalla y hacer algo ... tal vez unas pocas docenas de veces

Pero tenga en cuenta que hay una caída en la productividad al principio, así que comience a aprender las pruebas en algún proyecto donde la presión del tiempo aún no es una locura. Además, intente iniciarlo en un proyecto greenfield, el código heredado de pruebas unitarias es muy difícil y ayuda cuando sabe cómo se ve un buen conjunto de pruebas.

1
Christian Sauer

Un beneficio a menudo ignorado de TDD es que las pruebas actúan como una protección para asegurarse de que no está introduciendo nuevos errores cuando realiza un cambio.

El enfoque TDD es, sin duda, más tiempo al principio, pero el punto final es que escribirás menos código, lo que significa menos cosas que saldrán mal. Todas esas campanas y silbidos que a menudo incluye como rutina no llegarán a la base del código.

Hay una escena en la película Swordfish en la que, si la memoria sirve, un pirata informático tiene que trabajar con una pistola en la cabeza y está erm ... de lo contrario distraído. El punto es que es mucho más fácil trabajar cuando su espacio de cabeza está en el código y tiene tiempo de su lado en lugar de meses en el futuro con un cliente que le grita y otras prioridades se exprimen.

Los desarrolladores entienden que corregir errores más tarde es más costoso, pero le damos la vuelta. Si se le pagara $ 500 por día para codificar cómo codifica ahora o $ 1000 si escribiera de forma TDD, podría morder la mano de la persona que le hace la segunda oferta. Cuanto antes deje de ver las pruebas como una tarea rutinaria y lo vea como un ahorro de dinero, mejor estará.

1
Robbie Dee

Solo para complementar las respuestas anteriores: recuerde que la prueba no es un propósito en sí mismo. El propósito de realizar pruebas es que su aplicación se comporte como se espera a través de la evolución, en contextos inesperados, etc.

Por lo tanto, escribir pruebas no significa probar todos los comportamientos de todos los puntos finales de una entidad. Es un error común. Muchos desarrolladores piensan que necesitan probar todas las funciones/objetos/métodos/propiedades/etc. Esto conduce a una alta carga de trabajo y un montón de código y pruebas irrelevantes. Este enfoque es común en grandes proyectos, donde la mayoría de los desarrolladores no son conscientes del comportamiento holístico, pero solo pueden ver su dominio de interacción.

El enfoque correcto cuando se trata de recursos escasos y pruebas es bastante obvio y de sentido común, pero no se formaliza comúnmente: invierta recursos de desarrollo de pruebas primero en alto nivel funcionalidades, y gradualmente descienden en especificidades . Esto significa que, en algún momento, como desarrollador solitario, no solo se centraría en las pruebas unitarias, sino también en funcional/integración/etc. probando y dependiendo de sus recursos de tiempo, gradualmente en las principales funciones unitarias, como planificaría y consideraría. Las pruebas de alto nivel proporcionarán la información necesaria para abordar las pruebas de bajo nivel/unitarias y para planificar su estrategia de desarrollo de pruebas de acuerdo con los recursos que tenga.

Por ejemplo, le gustaría probar una cadena de procesamiento primero como una caja negra. Si encuentra que algún miembro de la cadena falla debido a que el comportamiento no consideró una condición extrema, escriba las pruebas que garanticen la funcionalidad no solo en este miembro sino también en otros. Entonces entregas. Para el siguiente ciclo, detecta que a veces la red falla. Entonces escribe pruebas que abordan ese problema en los módulos que podrían ser vulnerables. Y así.

0
RodolfoAP