¿Es normal pasar tanto tiempo, si no más, tiempo escribiendo pruebas que el código real?

203

Encuentro que las pruebas son mucho más complicadas y difíciles de escribir que el código real que están probando. No es raro que dedique más tiempo a escribir la prueba que al código que está probando.

¿Eso es normal o estoy haciendo algo mal?

Las preguntas “ son pruebas de unidad o desarrollo dirigido por pruebas ¿vale la pena? ”,“ Pasamos más tiempo implementando pruebas funcionales que implementando el sistema, ¿esto es normal? " y sus respuestas son más acerca de si las pruebas valen la pena (como en "¿deberíamos omitir las pruebas de escritura por completo?") . Aunque estoy convencido de que las pruebas son importantes, me pregunto si pasar más tiempo en las pruebas que en el código real es normal o si solo soy yo.

A juzgar por la cantidad de puntos de vista, respuestas y upvotes que recibí de mi pregunta, solo puedo asumir que es una preocupación legítima que no se aborda en ninguna otra pregunta del sitio web.

    
pregunta springloaded 14.10.2015 - 00:27

9 respuestas

198

Recuerdo de un curso de ingeniería de software, uno gasta alrededor del 10% del tiempo de desarrollo en escribir un nuevo código, y el otro 90% es depuración, pruebas y documentación.

Dado que las pruebas unitarias capturan la depuración y el esfuerzo de prueba en un código (potencialmente automatizable), tendría sentido que se haga un mayor esfuerzo en ellas; el tiempo real empleado no debería ser mucho más que la depuración y las pruebas que uno haría sin escribir las pruebas.

Por último, las pruebas también deben duplicar como documentación! Uno debe escribir pruebas unitarias en la forma en que el código está destinado a ser utilizado; es decir, las pruebas (y el uso) deben ser simples, poner las cosas complicadas en la implementación.

¡Si las pruebas son difíciles de escribir, es probable que el código que prueban sea difícil de usar!

    
respondido por el esoterik 14.10.2015 - 01:16
95

Es.

Incluso si solo realiza pruebas de unidad, no es raro tener más código dentro de las pruebas que el código realmente probado. No hay nada malo en ello.

Considera un código simple:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

¿Cuáles serían las pruebas? Hay al menos cuatro casos simples para probar aquí:

  1. El nombre de la persona es null . ¿Se lanza realmente la excepción? Eso es al menos tres líneas de código de prueba para escribir.

  2. El nombre de la persona es "Jeff" . ¿Obtenemos "Hello, Jeff!" en respuesta? Son cuatro líneas de código de prueba.

  3. El nombre de la persona es una cadena vacía. ¿Qué salida esperamos? ¿Cuál es la salida real? Pregunta adicional: ¿coincide con los requisitos funcionales? Eso significa otras cuatro líneas de código para la prueba de la unidad.

  4. El nombre de la persona es lo suficientemente corto para una cadena, pero es demasiado largo para combinarlo con "Hello, " y el signo de exclamación. ¿Qué pasa? ¹

Esto requiere una gran cantidad de código de prueba. Además, las piezas de código más elementales a menudo requieren un código de configuración que inicializa los objetos necesarios para el código en prueba, lo que a menudo también conduce a la escritura de talones y simulacros, etc.

Si la proporción es muy grande, en cuyo caso puede verificar algunas cosas:

  • ¿Hay duplicación de código en las pruebas? El hecho de que sea el código de prueba no significa que el código se deba duplicar (copiar y pegar) entre pruebas similares: tal duplicación dificultará el mantenimiento de esas pruebas.

  • ¿Hay pruebas redundantes? Como regla general, si elimina una prueba de unidad, la cobertura de la rama debería disminuir. Si no lo hace, puede indicar que la prueba no es necesaria, ya que las rutas ya están cubiertas por otras pruebas.

  • ¿Está probando solo el código que debe probar? No se espera que pruebe el marco subyacente de las bibliotecas de terceros, sino exclusivamente el código del propio proyecto. .

Con las pruebas de humo, las pruebas de sistema e integración, las pruebas funcionales y de aceptación y las pruebas de carga y carga, agrega aún más código de prueba, por lo que no debería preocuparse tener cuatro o cinco pruebas de LOC para cada LOC de código real. acerca de.

Una nota sobre TDD

Si le preocupa el tiempo que lleva probar su código, es posible que lo esté haciendo mal, ese es el código primero, y luego las pruebas. En este caso, TDD puede ayudarlo alentarlo a trabajar en iteraciones de 15 a 45 segundos, cambiando entre el código y las pruebas. De acuerdo con los defensores de TDD, acelera el proceso de desarrollo al reducir tanto la cantidad de pruebas que necesita realizar como, lo que es más importante, la cantidad de código de negocio que se debe escribir y, especialmente, reescribir para las pruebas. / p>

¹ Sea n la la longitud máxima de una cadena . Podemos llamar a SayHello y pasar por referencia una cadena de longitud n - 1 que debería funcionar bien. Ahora, en el paso Console.WriteLine , el formato debe terminar con una cadena de longitud n + 8, lo que resultará en una excepción. Posiblemente, debido a los límites de memoria, incluso una cadena que contenga n / 2 caracteres dará lugar a una excepción. La pregunta que debe hacerse es si esta cuarta prueba es una prueba unitaria (parece que es una, pero puede tener un impacto mucho mayor en términos de recursos en comparación con las pruebas unitarias promedio) y si prueba el código real o el marco subyacente.

    
respondido por el Arseni Mourzenko 14.10.2015 - 00:49
58

Creo que es importante distinguir entre dos tipos de estrategias de prueba: pruebas de unidad y pruebas de integración / aceptación.

Aunque la prueba de la unidad es necesaria en algunos casos, con frecuencia se realiza excesivamente sin remedio. Esto se ve agravado por métricas sin sentido forzadas en los desarrolladores, como "cobertura del 100%". enlace proporciona un argumento convincente para esto. Considere los siguientes problemas con las pruebas unitarias agresivas:

  • Una gran cantidad de pruebas sin sentido que no documentan un valor comercial, sino que simplemente existen para acercarse a esa cobertura del 100%. Donde trabajo, tenemos que escribir pruebas unitarias para fábricas que no hacen nada más que crear nuevas instancias de clases. No agrega ningún valor. O esos largos métodos .equals () generados por Eclipse, no hay necesidad de probarlos.
  • Para facilitar las pruebas, los desarrolladores subdividirían los algoritmos complejos en unidades verificables más pequeñas. Suena como una victoria, ¿verdad? No si necesitas tener 12 clases abiertas para seguir una ruta de código común. En estos casos, las pruebas unitarias pueden disminuir la legibilidad del código. Otro problema con esto es que si cortas tu código en partes demasiado pequeñas, terminas con una magnitud de clases (o partes de código) que parecen no tener justificación más allá de ser un subconjunto de otra pieza de código.
  • La refactorización de código altamente cubierto puede ser difícil, ya que también necesita mantener la abundancia de pruebas unitarias que dependen de que funcione solo . Esto se ve agravado por las pruebas de unidad de comportamiento, en las que parte de la prueba también verifica la interacción de los colaboradores de una clase (por lo general se burlan).

Por otra parte,

las pruebas de integración / aceptación son una parte muy importante de la calidad del software y, según mi experiencia, debería dedicar una cantidad significativa de tiempo a hacerlas bien.

Muchas tiendas han bebido el kool-aid TDD, pero, como se muestra en el enlace anterior, varios estudios muestran que su beneficio no es concluyente.

    
respondido por el firtydank 14.10.2015 - 09:58
10

No se puede generalizar.

Si necesito implementar una fórmula o un algoritmo a partir de una representación física, es muy posible que dedique 10 horas a pruebas unitarias paranoicas, ya que sé que el menor error o la imprecisión pueden llevar a errores que son casi imposibles de diagnosticar. , meses después.

Si solo quiero agrupar lógicamente algunas líneas de código y asignarle un nombre, que solo se usa en el ámbito del archivo, es posible que no lo pruebe en absoluto (si insiste en escribir pruebas para cada función, sin excepción, los programadores podría volver a escribir tan pocas funciones como sea posible).

    
respondido por el phresnel 14.10.2015 - 10:38
3

Sí, es normal si estás hablando de TDDing. Cuando tiene pruebas automatizadas, asegura el comportamiento deseado de su código. Cuando escribe sus pruebas primero, determina si el código existente ya tiene el comportamiento que desea.

Esto significa que:

  • Si escribe una prueba que falla, entonces corregir el código con la cosa más simple que funciona es más corto que escribir la prueba.
  • Si escribe una prueba que pasa, entonces no tiene ningún código adicional para escribir, de hecho, dedica más tiempo a escribir la prueba que al código.

(Esto no tiene en cuenta la refactorización del código, que apunta a dedicar menos tiempo a escribir el código posterior. Se equilibra mediante la refactorización de la prueba, que apunta a dedicar menos tiempo a escribir las pruebas posteriores).

Sí, también, si está hablando de escribir exámenes después del hecho, entonces pasará más tiempo:

  • Determinar el comportamiento deseado.
  • Determinar formas de probar el comportamiento deseado.
  • Cumpliendo con las dependencias del código para poder escribir las pruebas.
  • Código de corrección para las pruebas que fallan.

Que gastarás realmente escribiendo código.

Entonces, sí, es una medida esperada.

    
respondido por el Laurent LA RIZZA 14.10.2015 - 10:37
3

Me parece que es la parte más importante.

La prueba de unidad no siempre se trata de "ver si funciona bien", se trata de aprender. Una vez que prueba algo lo suficiente, se convierte en un "codificado" en su cerebro y eventualmente reduce el tiempo de prueba de su unidad y puede encontrarse escribiendo clases y métodos completos sin probar nada hasta que haya terminado.

Es por esto que una de las otras respuestas en esta página menciona que en un "curso" hicieron un 90% de pruebas, porque todos necesitaban aprender las advertencias de su objetivo.

La prueba de unidad no solo es un uso muy valioso de su tiempo, ya que literalmente mejora sus habilidades, es una buena manera de revisar su propio código nuevamente y encontrar un error lógico en el camino.

    
respondido por el Jesse 14.10.2015 - 06:16
2

Puede ser para muchas personas, pero depende.

Si está escribiendo las pruebas primero (TDD), es posible que esté experimentando alguna superposición en el tiempo dedicado a escribir la prueba; de hecho, es útil escribir el código. Considera:

  • Determinación de resultados y entradas (parámetros)
  • Convenciones de nomenclatura
  • Estructura: dónde colocar las cosas.
  • viejo pensamiento llano

Al escribir pruebas después de escribir el código, puede descubrir que su código no es fácil de probar, por lo que escribir pruebas es más difícil / toma más tiempo.

La mayoría de los programadores han estado escribiendo código mucho más tiempo que las pruebas, por lo que espero que la mayoría de ellos no sean tan fluidos. Además, puede agregar el tiempo necesario para comprender y utilizar su marco de prueba.

Creo que debemos cambiar nuestra forma de pensar acerca de cuánto tiempo lleva la codificación y cómo se involucran las pruebas unitarias. Nunca lo mire a corto plazo y nunca compare el tiempo total para ofrecer una característica particular, ya que debe tener en cuenta que no solo está escribiendo código mejor / menos con errores, sino que es un código que es más fácil de cambiar y aún lo hace. Mejor / menos buggy.

En algún momento, todos somos capaces de escribir código que es tan bueno, por lo que algunas de las herramientas y técnicas solo pueden ofrecer mucho para mejorar nuestras habilidades. No es como si pudiera construir una casa si solo tuviera una sierra guiada por láser.

    
respondido por el JeffO 14.10.2015 - 01:13
2
  

¿Es normal gastar tanto, si no más, tiempo escribiendo pruebas que el código real?

Sí, lo es. Con algunas advertencias.

En primer lugar, es "normal" en el sentido de que la mayoría de las tiendas grandes funcionan de esta manera, por lo que incluso si esta forma fuera completamente equivocada y tonta, aún así, el hecho de que la mayoría de las tiendas grandes funcione de esta manera lo hace "normal" .

Con esto no quiero decir que la prueba sea incorrecta. He trabajado en entornos sin pruebas y en entornos con pruebas obsesivo-compulsivas, y aún puedo decirles que incluso las pruebas obsesivo-compulsivas fueron mejores que ninguna prueba.

Y aún no hago TDD, (quién sabe, podría hacerlo en el futuro), pero hago la mayoría de mis ciclos de edición-ejecución-depuración ejecutando las pruebas, no la aplicación real, así que naturalmente, Trabajo mucho en mis pruebas, para evitar tener que ejecutar la aplicación real lo más posible.

Sin embargo, tenga en cuenta que existen peligros en las pruebas excesivas y, específicamente, en la cantidad de tiempo que se invierte en mantener las pruebas. (Estoy escribiendo principalmente esta respuesta para señalar específicamente esto).

En el prefacio de El arte de las pruebas unitarias de Roy Osherove (Manning, 2009) el autor admite haber participado en un proyecto que fracasó en gran parte debido a la tremenda carga del desarrollo impuestas por pruebas unitarias mal diseñadas que debían mantenerse durante todo el esfuerzo de desarrollo. Entonces, si pasa mucho tiempo haciendo nada más que mantener sus pruebas, esto no significa necesariamente que esté en el Camino correcto, porque es "normal". Es posible que su esfuerzo de desarrollo haya entrado en un modo poco saludable, donde podría ser necesario un replanteamiento radical de su metodología de prueba para salvar el proyecto.

    
respondido por el Mike Nakis 06.12.2015 - 17:47
0
  

¿Es normal gastar tanto, si no más,   ¿Pruebas de escritura de tiempo que código real?

  • Sí para escribir pruebas unitarias (Pruebe un módulo de forma aislada) si el código es altamente acoplado (legado, no es suficiente separación de problemas , falta inyección de dependencia , no tdd desarrollado )
  • Sí para escribir integración / aceptación-pruebas si la lógica a probar solo es accesible a través de gui-code
  • No para escribir pruebas de integración / aceptación siempre que el código de GUI y la lógica de negocios estén separados (la prueba no necesita interactuar con el GUI)
  • No para escribir pruebas unitarias si hay una serie de inquietudes, inyección de dependencia, el código fue desarrollado por test Testive (tdd)
respondido por el k3b 14.10.2015 - 10:36

Lea otras preguntas en las etiquetas