¿Es razonable no escribir pruebas unitarias porque tienden a comentarse más tarde o porque las pruebas de integración son más valiosas?

35

Estaba discutiendo las pruebas de unidad / integración con un colega, y él hizo un caso interesante en contra de escribir pruebas de unidad. Soy un gran defensor de la prueba de unidad (principalmente JUnit), pero estoy interesado en escuchar las opiniones de los demás, ya que hizo algunos puntos interesantes.

Para resumir sus puntos:

  • Cuando se producen cambios importantes en el código (nuevo conjunto de POJO, refactorización de aplicaciones importantes, etc.), las pruebas unitarias tienden a comentarse en lugar de modificarse.
  • Se dedica mejor tiempo a las pruebas de integración que cubren casos de uso, lo que hace que las pruebas de menor alcance sean menos / nada importantes.

¿Pensamientos sobre esto? Todavía estoy a prueba de unidad (como lo veo constantemente produciendo código mejorado), aunque las pruebas de integración suenan al menos tan valiosas.

Jeff Levine
fuente
99
Lo dijiste tú mismo: escribir pruebas unitarias produce un mejor código, porque te obliga a escribir código que sea comprobable. El código comprobable por unidad tiene muchas propiedades muy importantes que mejoran su calidad general.
Robert Harvey
1
@Robert Harvey - De acuerdo - Me interesaría ver una lista de estas propiedades.
Jeff Levine
18
En cuanto al primer punto, básicamente estás diciendo que un inconveniente de las pruebas unitarias es que no funcionan cuando no las usas. Puedes decir lo mismo sobre cualquier otra práctica. Además, su uso de la voz pasiva pasa por alto el hecho de que alguien está comentando activamente las pruebas unitarias. Por lo tanto, parece que no tiene la aceptación de los desarrolladores del equipo, lo cual es un problema diferente.
JacquesB
1
relacionado: programmers.stackexchange.com/a/301540/134647
Nick Alexeev

Respuestas:

62

Tiendo a estar del lado de tu amigo porque con demasiada frecuencia, las pruebas unitarias prueban cosas incorrecta .

Las pruebas unitarias no son inherentemente malas. Pero a menudo prueban los detalles de implementación en lugar del flujo de entrada / salida. Terminas con pruebas completamente inútiles cuando esto sucede. Mi propia regla es que una buena prueba de unidad te dice que acabas de romper algo; una mala prueba unitaria simplemente te dice que acabas de cambiar algo.

Un ejemplo fuera de mi cabeza es una prueba que se metió en WordPress hace unos años. La funcionalidad que se estaba probando giraba en torno a filtros que se llamaban entre sí, y las pruebas verificaban que las devoluciones de llamada se llamarían en el orden correcto. Pero en lugar de (a) ejecutar la cadena para verificar que las devoluciones de llamada se llamen en el orden esperado, las pruebas se centraron en (b) leer algún estado interno que posiblemente no debería haber sido expuesto para comenzar. Cambie las partes internas y (b) se ponga rojo; mientras que (a) solo se vuelve rojo si los cambios en las partes internas rompen el resultado esperado al hacerlo. (b) fue claramente una prueba inútil en mi opinión.

Si tiene una clase que expone algunos métodos al mundo exterior, lo correcto para probar en mi opinión son los últimos métodos solamente . Si también prueba la lógica interna, puede terminar exponiendo la lógica interna al mundo exterior, utilizando métodos de prueba complicados o con una letanía de pruebas unitarias que invariablemente se rompen cada vez que desea cambiar algo.

Dicho todo esto, me sorprendería si su amigo es tan crítico con las pruebas unitarias per se como parece sugerir. Más bien deduciría que es pragmático. Es decir, observó que las pruebas unitarias que se escriben no tienen sentido en la práctica. Cita: "las pruebas unitarias tienden a comentarse en lugar de reelaborarse". Para mí, hay un mensaje implícito allí: si tienden a necesitar una nueva revisión, es porque tienden a apestar. Suponiendo que sí, la segunda proposición sigue: los desarrolladores perderían menos tiempo escribiendo código que es más difícil de equivocar, es decir, pruebas de integración.

Como tal, no se trata de que uno sea mejor o peor. Es solo que es mucho más fácil equivocarse, y de hecho muy a menudo se equivoca en la práctica.

Denis de Bernardy
fuente
13
Esto, esto, cien veces esto. Esto es una gran cosa sobre el desarrollo impulsado por pruebas. Escribir pruebas primero lo ayudará a escribir pruebas que no son detalles de prueba de su implementación, sino el comportamiento previsto que estaba tratando de implementar.
wirrbel
99
Creo que las pruebas unitarias frágiles no son un rasgo inherente de las pruebas unitarias, sino que en lugar de malinterpretar para qué están ahí las pruebas unitarias. Lo que necesitamos aquí es una mejor educación para los desarrolladores. Si prueba los detalles de implementación de la unidad, lo está haciendo mal . Si hace públicos los métodos privados "para probarlos", lo está haciendo mal . Siempre pruebe la interfaz y el comportamiento públicos, que deberían cambiar con menos frecuencia que los detalles de implementación.
Andres F.
2
@DocBrown: De hecho no. Lo que quise decir es que a menudo se hace mal que el colega de OP lo hace bien en la mayoría de los casos, o al menos en todos los casos en los que me he encontrado en empresas obsesionadas con las pruebas unitarias.
Denis de Bernardy
3
@DenisdeBernardy: mi punto es: todo lo que escribió seguramente es correcto con mucha sabiduría de la práctica, pero me falta una conclusión de lo que eso significa para el caso de los OP con su colega, en el contexto de la sugerencia de "pruebas de integración como el mejor alternativa ".
Doc Brown
55
Estoy totalmente de acuerdo con Doc Brown. Esencialmente, su posición parece ser que las pruebas unitarias son buenas, pero cuando están mal escritas, se convierten en una molestia. Por lo tanto, no está de acuerdo con el colega de OP en absoluto, simplemente que debe asegurarse de escribir pruebas de unidad antes de afirmar su utilidad. Creo que su respuesta es muy confusa ya que la primera oración dice que está de acuerdo con el colega de OP cuando no parece ser el caso. Parece más una queja sobre las pruebas unitarias mal escritas (lo cual es un problema en la práctica, lo reconozco), no un caso en contra de las pruebas unitarias en general.
Vincent Savard
38

Cuando se producen cambios importantes en el código, las pruebas unitarias tienden a comentarse en lugar de modificarse.

Con un grupo indisciplinado de codificadores de vaqueros que piensan que todas las pruebas se vuelven "verdes" cuando se comentan todas las pruebas existentes: por supuesto. La reelaboración de las pruebas unitarias requiere la misma disciplina que escribirlas de primera mano, por lo que debe trabajar aquí en la "definición de hecho" de su equipo.

Se dedica mejor tiempo a las pruebas de integración que cubren casos de uso que hacen que las pruebas de menor alcance sean menos / nada importantes.

Eso no es completamente incorrecto ni completamente correcto. El esfuerzo para escribir pruebas de integración útiles depende mucho de qué tipo de software está escribiendo. Si tiene un software donde las pruebas de integración pueden escribirse casi tan fácilmente como las pruebas unitarias, se ejecutan aún rápido y le brindan una buena cobertura de su código, entonces su colega tiene razón. Pero para muchos sistemas, he visto que estos tres puntos no pueden cumplirse fácilmente mediante pruebas de integración, es por eso que debe esforzarse por tener también pruebas unitarias.

Doc Brown
fuente
Así es como me sentí mientras lo discutíamos. Suponiendo que se puedan escribir pruebas de integración completas y relevantes para un caso de uso, parece que la prueba unitaria sería un punto discutible. (Aunque ahora que escribo esto, estoy pensando en casos futuros inesperados, como que nuestro backend se convierta en un servicio web para otra aplicación).
Jeff Levine
+1 Para "tienes que trabajar en tu definición de hecho". ¡Bien dicho!
Andres F.
14

No sé si acepto los argumentos de tu colega.

  1. Si se comentan las pruebas unitarias, es porque alguien está siendo flojo. Esa no es la falla de las pruebas de la unidad. Y si comienzas a usar perezoso como excusa, puedes argumentar en contra de casi cualquier práctica que requiera un poco de esfuerzo.
  2. Si lo está haciendo bien, las pruebas unitarias no tienen que tomar mucho tiempo. No te impiden hacer un trabajo más importante. Si todos estamos de acuerdo en que arreglar el código roto es más importante que cualquier otra cosa, una prueba unitaria es muy importante. Es una manera fácil de probar las condiciones de publicación. Sin él, ¿cómo sabes que has cumplido las garantías de la condición posterior? Y si no lo has hecho, tu código está roto.

Hay otros argumentos más convincentes contra las pruebas unitarias. Bueno, no exactamente en contra de ellos. Comience con el daño de diseño inducido por la prueba de DHH . Es un escritor divertido, así que léelo. Lo que tomé de él:

Si realiza grandes cambios de diseño para acomodar las pruebas de su unidad, pueden obstaculizar otros objetivos en su proyecto. Si es posible, pregunte a una persona imparcial qué enfoque es más fácil de seguir. Si su código altamente comprobable es difícil de entender, puede perder todos los beneficios que obtuvo de las pruebas unitarias. La sobrecarga cognitiva de demasiada abstracción te ralentiza y facilita los errores de fugas. No lo descartes.

Si quieres ser matizado y filosófico, hay muchos recursos excelentes:

Roger escaso
fuente
+1 para ese artículo, podría ser una mejor respuesta para el caso de los OP que todo lo que podemos escribir aquí
Doc Brown
+1 para el punto número 1 sobre la pereza como excusa. No puedo enfatizar lo suficiente lo frustrante que es eso. He estado allí. Recientemente, en realidad.
Greg Burghardt
3
La razón 1 no es culpa de las pruebas unitarias, pero sigue siendo una razón contra las pruebas unitarias.
user253751
Después de leer el artículo de DHH, asegúrese de buscar los videos donde discutió el tema con Kent Beck y Martin Fowler (están en youtube): muchas personas parecen obtener una versión más extrema de lo que pretendía, y Lo refina bastante en estas discusiones.
Julio
6

Cuando se producen cambios importantes en el código (nuevo conjunto de POJO, refactorización de aplicaciones importantes, etc.), las pruebas unitarias tienden a comentarse en lugar de modificarse.

No hagas eso. Si encuentra a alguien que hace eso, asesínelos y asegúrese de que no se reproduzcan, ya que sus hijos podrían heredar ese gen defectuoso. La clave tal como la veo al ver programas de televisión CSI es dispersar muchas muestras de ADN engañosas en todas partes. De esta manera, contaminas la escena del crimen con tanto ruido que, dada la naturaleza costosa y lenta de las pruebas de ADN, la probabilidad de que te lo identifiquen es astronómicamente baja.


fuente
4

las pruebas unitarias tienden a comentarse en lugar de reelaborarse.

En ese momento, el proceso de revisión de código dice "repara las pruebas unitarias" y esto ya no es un problema :-) Si tu proceso de revisión de código no detecta este tipo de falla importante en el código enviado, entonces ese es el problema.

Philip Kendall
fuente
3

Cuando se producen cambios importantes en el código (nuevo conjunto de POJO, refactorización de aplicaciones importantes, etc.), las pruebas unitarias tienden a comentarse en lugar de modificarse.

Siempre trato de mantener la refactorización y el cambio de funcionalidad por separado. Cuando necesito hacer ambas cosas, generalmente primero cometo la refactorización.

Cuando se refactoriza el código sin cambiar la funcionalidad, se supone que las pruebas unitarias existentes ayudan a garantizar que la refactorización no rompa accidentalmente la funcionalidad. Entonces, para tal confirmación, consideraría deshabilitar o eliminar las pruebas unitarias como una señal de advertencia importante. Se debe decir a cualquier desarrollador que lo haga que no lo haga cuando se revise el código.

Es posible que los cambios que no cambian la funcionalidad sigan causando que las pruebas unitarias fallen debido a pruebas unitarias defectuosas. Si comprende el código que está cambiando, la causa de tales fallas en las pruebas unitarias suele ser inmediatamente obvia y fácil de solucionar.

Por ejemplo, si una función toma tres argumentos, una prueba unitaria que cubra la interacción entre los dos primeros argumentos para la función podría no haberse ocupado de proporcionar un valor válido para el tercer argumento. Este defecto en la prueba de la unidad puede quedar expuesto por una refactorización del código probado, pero es fácil de solucionar si comprende lo que se supone que debe hacer el código y lo que está probando la prueba de la unidad.

Al cambiar la funcionalidad existente, generalmente será necesario cambiar también algunas pruebas unitarias. En este caso, las pruebas unitarias ayudan a garantizar que su código cambie la funcionalidad según lo previsto y no tenga efectos secundarios no deseados.

Al corregir errores o agregar nuevas funcionalidades, generalmente es necesario agregar más pruebas unitarias. Para aquellos, puede ser útil confirmar las pruebas unitarias primero y confirmar la corrección de errores o la nueva funcionalidad más adelante. Eso hace que sea más fácil verificar que las nuevas pruebas unitarias no pasaron con el código anterior pero sí con el código más nuevo. Sin embargo, este enfoque no carece por completo de inconvenientes, por lo que también existen argumentos a favor de confirmar las nuevas pruebas unitarias y las actualizaciones de código simultáneamente.

Se dedica mejor tiempo a las pruebas de integración que cubren casos de uso, lo que hace que las pruebas de menor alcance sean menos / nada importantes.

Hay algún elemento de verdad en esto. Si puede obtener cobertura de las capas inferiores de la pila de software con pruebas dirigidas a las capas superiores de la pila de software, sus pruebas pueden ser más útiles al refactorizar el código.

Sin embargo, no creo que encuentre un acuerdo sobre la distinción exacta entre una prueba unitaria y una prueba de integración. Y no me preocuparía si tiene un caso de prueba que un desarrollador llama una prueba unitaria y otro llama una prueba de integración, siempre que puedan estar de acuerdo en que es un caso de prueba útil.

kasperd
fuente
3

Las pruebas de integración se realizan para verificar si el sistema se comporta como debería, lo que siempre tiene valor comercial. Las pruebas unitarias no siempre hacen eso. Las pruebas unitarias podrían estar probando código muerto, por ejemplo.

Existe una filosofía de prueba que dice que cualquier prueba que no le brinde información es un desperdicio. Cuando no se está trabajando en un fragmento de código, sus pruebas unitarias siempre (o casi siempre) serán verdes, lo que le dará poca o ninguna información.

Todos los métodos de prueba estarán incompletos. Incluso si obtiene una cobertura del 100% por alguna métrica, aún no está pasando por casi todos los posibles conjuntos de datos de entrada. Entonces, no pienses que las pruebas unitarias son mágicas.

A menudo he visto la afirmación de que tener pruebas unitarias mejora su código. En mi opinión, las mejoras que las pruebas unitarias traen al código están obligando a las personas que no están acostumbradas a romper su código en piezas pequeñas y bien pensadas. Si se acostumbra a diseñar su código en piezas pequeñas y bien factorizadas normalmente, es posible que ya no necesite pruebas unitarias para obligarlo a hacerlo.

Dependiendo de cuán pesado sea el examen unitario y cuán grande sea su programa, puede terminar con una enorme cantidad de exámenes unitarios. Si es así, los grandes cambios se vuelven más difíciles. Si un gran cambio causa muchas pruebas unitarias fallidas, puede negarse a hacer el cambio, hacer un gran trabajo para arreglar muchas pruebas o descartarlas. Ninguna de las opciones es ideal.

Las pruebas unitarias son una herramienta en la caja de herramientas. Están ahí para servirle, no al revés. Asegúrate de salir de ellos al menos tanto como lo pones.

Michael Shaw
fuente
3

Las pruebas unitarias definitivamente no son la bala de plata que algunos defensores afirman que son. Pero en general tienen una relación costo / beneficio muy positiva. No harán que su código sea "mejor", tal vez más modular. "mejor" tiene muchos más factores que lo que implica "comprobabilidad". Pero se asegurarán de que funcione en todos los casos y usos en los que pensó, y eliminarán la mayoría de sus errores (probablemente los más triviales).

Sin embargo, el mayor beneficio de las pruebas unitarias es que hacen que su código sea más flexible y resistente al cambio. Puede refactorizar o agregar funciones, y puede estar seguro de que no rompió nada. Esto solo hace que valga la pena para la mayoría de los proyectos.

Refiriéndose a los puntos hechos por su amigo:

  1. Si agregar POJO rompe las pruebas de su unidad, entonces probablemente estén muy mal escritas. Los POJO generalmente son el idioma que está utilizando para hablar sobre su dominio, y los problemas que está resolviendo, cambiarlos obviamente significa toda su lógica comercial, y probablemente el código de presentación debe cambiar. No puede esperar que lo que asegura que esas partes de su programa funcionen no cambie ...

  2. Quejarse de que las pruebas de la Unidad son malas, porque la gente las comenta, es como quejarse de que los cinturones de seguridad son malos porque la gente no los usa. Si alguien comenta el código de prueba y rompe algo, debería terminar en una conversación muy seria y desagradable con su jefe ...

  3. Las pruebas de integración son muy superiores a las pruebas unitarias. no hay duda. especialmente si estás probando un producto. Pero escribir buenas y completas pruebas de integración que le brinden el mismo valor, cobertura y seguridad de corrección que las pruebas unitarias le brindan es increíblemente difícil.

ALASKA_
fuente
2

Solo puedo pensar en un argumento en contra de las pruebas unitarias, y es bastante débil.

Las pruebas unitarias muestran la mayoría de su valor cuando refactoriza o cambia el código en una etapa posterior. Usted ve una gran reducción en el tiempo requerido para agregar funciones y asegurarse de no haber roto nada.

Sin embargo: hay un costo (pequeño) en tiempo de escribir pruebas en primer lugar.

Por lo tanto: si un proyecto es lo suficientemente corto y no requiere mantenimiento / actualizaciones continuas, es posible que el costo de escribir pruebas supere sus beneficios.

Ewan
fuente
+1 por intentar responder sinceramente a la pregunta que se hizo.
RubberDuck
2
El costo de la prueba unitaria definitivamente no es pequeño. Las buenas pruebas unitarias generalmente toman más tiempo para escribir que lo que prueban. La mayoría de las veces los beneficios exceden el costo.
AK_
1
solo en teoría: cuando refactoriza, termina con un costo enorme al actualizar las pruebas unitarias, ya que casi siempre son demasiado granulares. Si estaba hablando de tener un buen conjunto de pruebas de integración, entonces estaría en lo cierto.
gbjbaanb
Si un proyecto es lo suficientemente corto y no requiere mantenimiento / actualizaciones continuas , la mayoría de los sistemas heredados han comenzado como proyectos cortos sin pruebas, y terminan contratando a más desarrolladores para mantener un sistema desarrollado sin pruebas
Fabio