¿Cómo lidiar con un gran número de pruebas fallidas? [cerrado]

22

Estoy trabajando en el desarrollo de un antiguo proyecto escrito en Java. Tenemos más de 10 millones de LOC y, lo que es peor, más de 4000 pruebas funcionales.

Las pruebas, programadas por Hudson, están fallando como loco con cada cambio de código más grande. Verificación de la falla de la prueba: si se trata de un problema en el producto o en la prueba, lleva meses. ¡No podemos eliminar las pruebas anteriores porque no sabemos qué están probando!

¿Lo que podemos hacer? ¿Cómo proceder con tal cantidad de pruebas heredadas?

Hector brosuli
fuente
66
Las preguntas reales tienen respuestas. En lugar de explicar por qué su situación es terrible, o por qué su jefe / compañero de trabajo lo hace infeliz, explique lo que quiere hacer para mejorarlo. Para obtener más información, haga clic aquí ...
mosquito
13
¿Por qué permitiste que las pruebas comenzaran a fallar en primer lugar? BTW 4000 no es tantas pruebas para 10 MLOC
Bћовић
66
Dejar de caer y rodar.
Navin
13
Averigüe qué están probando las pruebas. Luego, vuelva a visitar y pregúntese, en primer lugar, cómo las pruebas en tierra requieren meses para encontrar un problema, y ​​también descubra cómo sus requisitos cambiaron tanto. Las pruebas están destinadas a encapsular los requisitos en una aplicación. Si sus pruebas fallan, su código no está funcionando de acuerdo con los requisitos, ya sea que los escribió incorrectamente o ninguno de sus códigos cumple con sus requisitos.
Dan Pantry
66
Todos hemos visto un compilador eliminar un millón de errores debido a un solo '}' perdido. Si se trata de pruebas funcionales con una gran cantidad de dependencias, ¿quizás el mismo tipo de problema esté funcionando?
Dan Pichelman

Respuestas:

37

Abandónalos.

Sé que es difícil dejar de lado algo que claramente fue un gran esfuerzo de producción, pero las pruebas no están funcionando para ti, están trabajando en tu contra. Se supone que un conjunto de pruebas le dará la confianza de que el sistema hace lo que se supone que debe hacer. Si no lo hace, es un pasivo en lugar de un activo. No importa si el sistema o las pruebas tienen la culpa: mientras el conjunto de pruebas indique grandes cantidades de errores, no puede cumplir su propósito.

Lo que necesita ahora es un nuevo conjunto de pruebas que se ejecute sin errores. Eso significa que inicialmente tendrá poca cobertura, de hecho casi ninguna cobertura. Cada vez que arreglas o te tomas el tiempo para comprender a fondo algo sobre tu sistema, repasas ese conocimiento en una prueba. Con el tiempo, esto producirá una nueva red de seguridad en la que puede construir en el futuro. Tratar de reparar una red de seguridad vieja y mal entendida es una pérdida de tiempo que casi nunca vale la pena.

Incluso recomendaría que no se transfieran pruebas de la suite anterior a la nueva. Claro, algunos de ellos pueden tener éxito ahora, pero ¿es porque están probando exactamente lo que se supone que deben probar, o simplemente porque algunos disparos aleatorios siempre dan en el blanco? Obviamente, debe ser pragmático sobre lo que se puede y no se puede hacer con el esfuerzo que tiene disponible para gastar, pero no se puede comprometer el principio de que un conjunto de pruebas debe ejecutarse limpiamente para hacer su trabajo .

Kilian Foth
fuente
99
No puedo ver la lógica en su punto: "Se supone que un conjunto de pruebas le da confianza de que el sistema hace lo que se supone que debe hacer. [...] Lo que necesita ahora es un nuevo conjunto de pruebas que se ejecute sin errores ". Si tiene un código defectuoso que hace que las pruebas fallen, no significa que deba volver a escribir las pruebas para que pase el código defectuoso.
DBedrenko
13
La situación de Héctor es que no sabe si el código o las pruebas están mal . Si lo hiciera, podría trabajar con la base del código y cambiar en algún momento las pruebas, a veces el código comercial. Tal como están las cosas, incluso este tipo de trabajo pesado no pagaría, ya que no sabes si estás solucionando problemas o si los estás cometiendo.
Kilian Foth
55
"Se supone que un conjunto de pruebas le da confianza de que el sistema hace lo que [debería]". No, se supone que debe decirme si el sistema hace lo que debería; La falsa confianza es peor que ninguna. "Lo que necesita es un conjunto de pruebas que se ejecute sin errores" No, lo que necesita es un conjunto de pruebas que le brinde información útil sobre la solidez del código. Lo que tiene ahora es muchas luces de advertencia crípticas, que son mejores que una luz verde de un nuevo y brillante conjunto de pruebas que no prueba nada. Debería deshabilitar temporalmente las pruebas anteriores , pero no abandonar ninguna que no haya verificado como espuria.
Beta
44
¡Esta respuesta es un consejo increíblemente malo! Si pequeños cambios en el código conducen a una gran cantidad de pruebas fallidas, probablemente tenga problemas de calidad de código. La prueba al menos te notificará que rompiste algo. Necesita mejorar el código (refactorizando cuidadosamente ayudado por pruebas). Si solo elimina las pruebas, no tiene forma de saber si rompe algo.
JacquesB
44
Este es un consejo terrible. Si el OP y su equipo ya no pueden entender la base del código y sus pruebas, descartar las pruebas y comenzar de nuevo es poco probable que resuelva el problema central del OP: comprender la base del código. Creo que podemos asumir que las pruebas funcionaron cuando se escribieron, por lo que su equipo necesita rastrear lo que cada prueba está probando y leer la fuente para determinar si es la base de código o la prueba que está mal hoy. Mucho más simple que comenzar de nuevo con pruebas mal guiadas y no informadas / ingenuas.
SnakeDoc
29

Ve y arregla las pruebas.

Su mayor error es que permitió que las pruebas fallaran, y obviamente lo ignoró por un tiempo. Lo que tiene no son "pruebas heredadas": está trabajando en un código heredado. Y considero que cada código escrito sin pruebas es heredado.


Verificación de la falla de la prueba: si se trata de un problema en el producto o en la prueba, lleva meses. ¡No podemos eliminar las pruebas anteriores porque no sabíamos lo que están probando!

Parece que hay un problema aún mayor en su organización, ya que no está trabajando con requisitos claros. No puedo entender que usted (u otra persona) no pueda confirmar el comportamiento correcto.

BЈовић
fuente
44
Eso es lo que idealmente debería hacerse, pero parece que las pruebas aquí son tan malas que los programadores ni siquiera saben lo que están probando. ¡Creo que en este caso puede ser mejor deshacerse de las pruebas WTF y comenzar a escribir nuevas y significativas de inmediato! En un proyecto reciente tuve un problema similar con un compañero de trabajo cuyas pruebas siempre fallaban sin buenas razones (no falló porque lo que se suponía que debía probarse salió mal, ¡sino porque el código de prueba era tan frágil y ni siquiera determinista!) . ¡Pasé días reescribiendo lo que pude, y destrocé el resto!
Shautieh
@Shautieh Las pruebas WTF no funcionan sin el código WTF, por lo que corregir las pruebas generalmente significa refactorizar el código. Y las pruebas que fallan al azar son el signo de incompetencia. Y el supervisor de su colega tiene la culpa de no hacer su trabajo.
Bћовић
2
A veces la vida es dura: el responsable de las pruebas (y el código) de WTF obtuvo el salario más alto del equipo (20% más que yo), y cuando renunció en medio del proyecto (porque encontró un trabajo mejor remunerado) ) Tuve que enfrentar a algunos de sus desarrolladores: / Pero tienes toda la razón al decir que nuestro supervisor también tuvo la culpa ^^
Shautieh
@Shautieh: un colega mío dijo una vez que un error en el código son dos errores: un error en el código y un punto ciego en las pruebas. Supongo que en realidad son tres si cuentas al desarrollador que tolera las pruebas fallidas, y cuatro si cuentas a los gerentes que promueven a un incompetente.
Beta
@Beta Suena bastante similar a la definición que a veces se usa en TDD: "Un error es una prueba que aún no ha escrito".
Restablece a Monica el
22

Las pruebas son valiosas. Por lo menos, registran que alguien consideró que deberían pasar tiempo escribiéndolos, por lo que presumiblemente tuvieron algún valor para alguien una vez. Con suerte, contendrán un registro completo de todas las características y errores en los que el equipo ha trabajado alguna vez, aunque también pueden haber sido una forma de obtener algún número de cobertura de prueba arbitraria sin haber sido cuidadosamente pensados. Hasta que los mire, no sabrá cuál es el caso aquí.

Si la mayoría de sus pruebas pasan la mayor parte del tiempo, simplemente muerda la bala e invierta el tiempo en averiguar qué intentaban hacer las pocas pruebas que fallaron, y corregirlas o mejorarlas para que el trabajo sea más fácil la próxima vez. En ese caso, avance a la sección Determinar la intención de cada prueba , para obtener algunos consejos sobre qué hacer con un pequeño número de pruebas reprobadas.

Por otro lado, es posible que ahora se enfrente con una construcción Roja, y cientos o incluso miles de pruebas que no han pasado por un tiempo, y Jenkins no ha sido Verde durante mucho tiempo. En este punto, el estado de compilación de Jenkins se ha vuelto inútil, y un indicador clave de problemas con su registro ya no funciona. Necesita arreglar esto, pero no puede permitirse el lujo de detener todo el progreso hacia adelante mientras arregla el desorden en su sala de estar.

Para mantener su cordura mientras realiza la arqueología requerida para determinar qué valor se puede recuperar de las pruebas que han fallado, recomiendo los siguientes pasos:

Desactiva temporalmente las pruebas fallidas.

Hay varias formas de hacerlo, dependiendo de su entorno, que no describe claramente, por lo que no puedo recomendar ninguna en particular.

Algunos marcos apoyan la noción de fallas esperadas. Si el suyo lo hace, entonces esto es genial, ya que verá una cuenta regresiva de cuántas pruebas quedan en esta categoría, e incluso se le informará si algunas de ellas comienzan a pasar inesperadamente.

Algunos marcos admiten grupos de prueba y le permiten decirle a Hudson que solo ejecute algunas de las pruebas o que omita un grupo de pruebas. Esto significa que ocasionalmente puede ejecutar el grupo de prueba manualmente para ver si alguno está pasando.

Algunos frameworks le permiten anotar o marcar pruebas individuales para ser ignoradas. En este caso, es más difícil ejecutarlos como grupo, pero evita que te distraigan.

Puede mover las pruebas a un árbol de origen que normalmente no se incluye en la compilación.

In extremis, puede eliminar el código del HEAD del sistema de control de versiones, pero esto hará que sea más difícil de reconocer cuando se haya completado la tercera fase.

El objetivo es lograr que Jenkins se ponga verde lo antes posible, para que pueda comenzar a moverse en la dirección correcta lo antes posible.

Mantenga las pruebas relevantes.

Resuelva agregar nuevas pruebas a medida que agrega o modifica código, y comprométase a mantener todas las pruebas aprobadas.

Las pruebas pueden fallar por una variedad de razones, incluido el hecho de que no eran pruebas bien escritas para comenzar. Pero una vez que consigues que Jenkins sea verde, mantenerlo así es realmente importante.

Acostúmbrate a escribir buenas pruebas y haz que sea un gran problema si las pruebas comienzan a fallar.

Determine la intención de cada prueba.

Ir a través de las pruebas desactivadas una por una. Comience con los que afectan los módulos que cambia con más frecuencia. Determine la intención de la prueba y la razón del fracaso.

  • ¿Prueba una función que se eliminó de la base del código a propósito? Entonces probablemente puedas eliminarlo.

  • ¿Está atrapando un error que nadie ha notado todavía? Vuelva a instalar la prueba y corrija el error.

  • ¿Está fallando porque estaba haciendo suposiciones injustificadas (por ejemplo, suponiendo que el texto del botón siempre estaría en inglés, pero ahora ha localizado su aplicación para varios idiomas)? Luego, descubra cómo hacer que la prueba se enfoque en una sola cosa y aislarla de los cambios no relacionados lo mejor que pueda.

  • ¿La prueba se extiende por toda la aplicación y representa una prueba del sistema? Luego, retírelo del conjunto de pruebas principal de Jenkins y agréguelo al conjunto de Regresión que se ejecuta con menos frecuencia.

  • ¿La arquitectura de la aplicación ha cambiado más allá del reconocimiento, por lo que la prueba ya no hace nada útil? Bórralo.

  • ¿Se agregó la prueba para aumentar artificialmente las estadísticas de cobertura de código, pero en realidad no hace más que confirmar que el código se compila correctamente y no entra en un bucle infinito? O bien, ¿la prueba simplemente confirma que el marco de simulación seleccionado devuelve los resultados que acaba de decirle? Bórralo.

Como resultado de esto, algunas pruebas se mantendrán, algunas se modificarán, algunas se dividirán en múltiples fragmentos independientes de tamaño de bocado y algunas se eliminarán. Mientras sigas progresando con los nuevos requisitos, es responsable dedicar un poco de tiempo a lidiar con deudas técnicas como esta.

Bill Michell
fuente
1
¡Es una muy, muy mala idea deshabilitar las pruebas solo porque fallan! El resto de tus consejos son buenos, pero no esto. Las pruebas que no comprende nunca deben deshabilitarse. El punto de prueba no es obtener una barra verde, ¡el punto es obtener un software que funcione!
JacquesB
Depende de la escala del problema. Pero estoy de acuerdo, en realidad no lo he dejado claro.
Bill Michell
Se agregó un párrafo para diferenciar entre "somos verdes pero cada cambio hace que las cosas se pongan rojas" y "hemos estado rojos tanto tiempo que hemos olvidado cómo se ve el verde"
Bill Michell,
En lugar de deshabilitar o incluso eliminar la prueba, algunos marcos también proporcionan la noción de una falla esperada . Esto podría ayudar a aumentar la SNR porque recibirás una alerta más directa sobre nuevas fallas (lo que no harás si siempre hay una gran cantidad de fallas), pero aún así serás notificado sobre las fallas conocidas y, quizás aún más importante, cuando un anteriormente la prueba que falla pasa de repente de nuevo. Si se leen las fallas inesperadas y las fallas esperadas en naranja, haga que las pruebas en rojo sean las verdes primero y que las naranjas verdes sean su segunda prioridad.
5gon12eder
11

4000 pruebas es un problema insoluble. 40 pruebas es más manejable. Seleccione aleatoriamente un número manejable de pruebas para ejecutar y analizar. Clasifique los resultados como:

  1. Prueba inútil
  2. Prueba útil que funciona limpia
  3. Prueba útil que falla

Si muchas de las pruebas caen en la primera categoría, puede ser hora de descartar su conjunto de pruebas actual y armar una útil para el código actual.

Si muchas de las pruebas fallan de una manera que le informa sobre un problema en su código, debe trabajar a través de las pruebas fallidas para solucionar los problemas. Puede encontrar que corregir uno o dos errores hace que se ejecute una gran cantidad de pruebas.

Patricia Shanahan
fuente
2
+ (int) (PI / 3) para proporcionar una forma real y simple de probar el conjunto de pruebas, aunque estoy de acuerdo en que, como regla general, las pruebas como las descritas por OP son un signo de un diseño defectuoso, pero sin pruebas lo que está mal, cualquier consejo sobre el conjunto de pruebas en sí mismo (ya sea "abandonarlos", "arreglar las pruebas", "escribir nuevas pruebas") es simplemente inútil. Exactamente como dices: si tuviera 4k pruebas y 40 de ellas completamente al azar de esas 3/4 fueran una mierda y fueran inútiles, no dudaría en deshacerme de toda la suite. Si 3/4 de ellos hubieran sido realmente útiles, los dejaría y me enfocaría en mejorar el código.
vaxquis
7

Si esta afirmación es cierta,

Las pruebas ... fallan como locos con cada cambio de código más grande.

entonces eso implica que si regresa al código justo antes de un "cambio de código más grande", entonces muchas de las pruebas pasarán nuevamente. Después de hacer eso, tome una pequeña parte de los cambios y vea qué pruebas están fallando recientemente. Esto lo ayudará a aislar mejor qué cambios en el código están causando qué pruebas fallan. Para cada prueba, una vez que haya aislado el problema, debería poder determinar si el nuevo código tenía fallas o si la prueba lo era. Si es un problema con el nuevo código, asegúrese de compararlo con la última versión en caso de que ese error en particular ya se haya solucionado.

Repita hasta que tenga la última base de código.

Esto puede parecer una tarea abrumadora, pero es muy probable que una vez que siga este camino y comience a aislar algunos de los problemas, comenzará a surgir un patrón que puede acelerar enormemente el proceso. Por ejemplo:

  • Puede notar que muchas pruebas dependen de otra cosa que es defectuosa. Arreglar esa pieza puede arreglar muchas pruebas.
  • Puede notar que muchas pruebas son defectuosas y deben repararse o eliminarse.
  • Puede notar que un desarrollador en particular tiene una frecuencia mucho mayor de causar que las pruebas se rompan. Ese desarrollador puede necesitar más capacitación o supervisión.
TTT
fuente
3

Si no sabe lo que están probando, retírelos hasta que lo sepa. ¡Las pruebas son cosas fluidas, si elimina una función que ya no es necesaria, entonces debería esperar tener que cambiar la prueba que prueba esa función! Entonces, a menos que sepa lo que están probando las pruebas, no tiene la esperanza de cambiar la base de código con ellas.

Puede configurar el sistema de prueba en las máquinas del desarrollador y ejecutarlo allí para que los desarrolladores puedan ver con qué partes interactúan las pruebas, ojalá proporcionen esta documentación faltante y se familiaricen con la base de código que no está cambiando correctamente o no prueba más larga correctamente.

En resumen: si sus pruebas anteriores fallan cuando realiza cambios, los cambios en su código no son buenos. Use esas pruebas como un medio de educación sobre cómo funciona el sistema.

gbjbaanb
fuente
1
Es por eso que me gusta la @Ignoreanotación de JUnit : puede mantener sus pruebas, pero no ejecutarlas. Entonces es simplemente una cuestión de volver a habilitarlos y arreglarlos uno a la vez. Le permite reducir su enfoque a un puñado de pruebas a la vez, en lugar de sentirse abrumado por miles de fallas.
TMN
1
Este es un mal consejo. No debe eliminar ni deshabilitar una prueba que no comprende. Sólo si lo hace entender la prueba, y se tiene la certeza que pone a prueba una característica obsoleta, que debe ser desactivado o eliminado.
JacquesB
2

Lo más importante que haría es volver a los fundamentos de lo que se supone que deben hacer las pruebas y lo que la empresa necesita para seguir avanzando. El trabajo de las pruebas es identificar los problemas antes de que se vuelvan costosos de solucionar más adelante. Creo que la palabra clave en esa oración es "costosa". Estos problemas necesitan una solución comercial. ¿Aparecen problemas costosos en el campo? Si es así, las pruebas están fallando directamente.

Su gestión y usted deben realizar una verificación de la realidad. Usted está descubriendo que los costos de desarrollo se están disparando debido a un conjunto heredado de pruebas. ¿Cómo se comparan esos costos con los costos de entregar un producto defectuoso porque deshabilitó las pruebas? ¿Cómo se comparan con la tarea onerosa de descubrir qué comportamientos necesitan los usuarios (cuáles son las cosas que deben probarse)?

Estos son problemas que necesitan soluciones comerciales porque afectan el lado comercial del trabajo. Está entregando el producto a un cliente, y ese es un límite en el que la empresa está muy interesada. Pueden identificar soluciones que usted, como desarrollador, no puede. Por ejemplo, puede ser razonable para ellos proporcionar dos productos: un producto "heredado" para aquellos que necesitan confiabilidad y están dispuestos a renunciar a nuevas funciones, con un producto "visionario" que puede tener más fallas, pero es pionero en el futuro. Esto le daría la oportunidad de desarrollar dos conjuntos independientes de pruebas ... una heredada con 4000 pruebas y otra con más de las pruebas que cree que deben hacerse (y documentarlas para que este proceso no se repita).

Entonces, comienza el arte: ¿cómo puedes manejar a esta bestia de dos cabezas para que los avances en una rama también ayuden a la otra rama? ¿Cómo pueden sus actualizaciones a la rama "visonaria" regresar a la rama "heredada", a pesar de los estrictos requisitos de prueba? ¿Cómo pueden las continuas solicitudes de los clientes en la rama "heredada" moldear mejor su comprensión de los requisitos que necesitarían sus clientes heredados si finalmente volviera a fusionar los productos?

Cort Ammon - Restablece a Monica
fuente
-3

¡No podemos eliminar las pruebas anteriores porque no sabíamos lo que están probando!

¡Es exactamente por eso que debes eliminar las pruebas anteriores! Si no sabes lo que están haciendo, entonces el fracaso no tiene sentido y ejecutarlos es una pérdida de tiempo. Tíralos y comienza de nuevo.

Mohair
fuente
2
esto parece punto de repetición simplemente ya realizados y se explica en la parte superior respuesta
mosquito
44
El fracaso no es "sin sentido", significa que no comprende el sistema tan bien como creía que lo hacía.
Ben Voigt
La falla definitivamente no tiene sentido aquí, porque el OP declaró claramente que no entienden el sistema.
Mohair