¿Alternativa al indicador "Pasar / Construcción rota"?

14

Cuando se realiza una integración continua ejecutando las pruebas en cada confirmación, una práctica recomendada común es hacer que todas las pruebas pasen en todo momento (también conocido como "no rompa la compilación").

Encuentro algunos problemas con eso:

Por ejemplo, uno no puede ayudar a un proyecto de código abierto creando pruebas correspondientes a tickets. Sé que si propongo una Solicitud de extracción a un proyecto de código abierto que contenga una prueba fallida, la compilación se marcará como fallida y el proyecto no querrá que se fusione en su repositorio porque "rompería la compilación".

Y no creo que sea malo tener pruebas fallidas en su repositorio , es como tener problemas abiertos en su rastreador. Estas son solo cosas que esperan ser reparadas.

Lo mismo ocurre en una empresa. Si trabaja con TDD, no puede escribir pruebas, comprometerse y luego escribir el código lógico que cumple la prueba. Eso significa que si he escrito 4-5 pruebas en mi computadora portátil, no puedo enviarlas antes de irme de vacaciones. Nadie puede recuperar mi trabajo. Ni siquiera puedo "compartirlos" con un colega, excepto enviándolos por correo electrónico, por ejemplo. También evita trabajar con una persona que escribe las pruebas y la otra que escribe el modelo.

Todo eso para decir, ¿estoy haciendo mal uso / malentendido el proceso de construcción / integración continua? Me parece que "pasar" / "no pasar" es un indicador demasiado limitado.

¿Hay alguna manera de hacer que la integración continua y la TDD sean compatibles?

¿Tal vez hay una solución / práctica estándar para distinguir "nuevas pruebas" (que pueden fallar) y "pruebas de regresión" (que no deberían fallar porque solían funcionar)?

Matthieu Napoli
fuente
1
Tenga un indicador que muestre si el número de pruebas fallidas aumentó (rojo) o disminuyó (verde) en la última hora (más o menos).
Joachim Sauer
2
No soy un especialista en TDD / ALM (de ahí el comentario, en lugar de una respuesta), pero creo que su problema se puede resolver con ramas privadas / ramas de características. ¿Estás trabajando en la función A? Ramifíquelo, trabaje en la sucursal (con colegas), y una vez que haya terminado, combínelo en el tronco continuamente integrado.
Avner Shahar-Kashtan
@JoachimSauer Sí, pero ¿esta métrica está estandarizada / utilizada en algún proyecto importante? Estoy tratando de entender por qué la mayoría de los proyectos (y las herramientas de CI) funcionan de esa manera.
Matthieu Napoli
Creo que el criterio correcto para "pruebas que pueden fallar" no es "nuevas pruebas", sino "pruebas para problemas abiertos conocidos". Puedo ver cómo es útil tener esas pruebas: también puedo ver cómo esas pruebas NO son útiles en la compilación de CI porque contaminan el significado de la prueba aprobada / reprobada allí (solo desea ejecutar pruebas para las que alguien realmente ha pasado tiempo para hacerlos pasar).
Joris Timmermans
@MadKeithV Exactamente
Matthieu Napoli

Respuestas:

12

Veo a dónde se dirige, pero este tipo de problemas generalmente se resuelve de otras maneras. Hay una buena razón por la cual este es un protocolo estándar. Si alguien envía un código que no se compila, todos los que actualicen su código tendrán un programa que no se compila . Eso incluye a los programadores que actualmente están trabajando en algo completamente diferente y de alguna manera se encuentran en una situación en la que necesitan esperar antes de poder compilar y probar en qué están trabajando.

El protocolo estándar es que puede confirmar los cambios incluso para un trabajo completo o incluso incompleto, siempre que se compile para que los programadores puedan actualizar su código todos los días si es necesario.

Sin embargo, todavía veo a qué te refieres. A veces desea comprometerse para simplemente guardar su código. Para esto, la mayoría de los repositorios de origen admiten ramificaciones. Esto le permite crear una rama privada, trabajar en ella sin molestar a los demás y luego fusionarse en el tronco cuando finalice el trabajo. Esto le permite comprometerse cuando lo desee sin ninguna reacción violenta asociada con la ruptura de la compilación.

Si eso no es adecuado, GIT le permite comprometerse (empujar) a repositorios en su máquina local, pero posiblemente el repositorio podría estar en cualquier lugar. Puede crear un repositorio para trabajo potencialmente parcial / incompleto y otro repositorio para trabajo terminado, y en ese repositorio puede agregar una compilación nocturna.

Nuevamente, no puedo enfatizar la importancia lo suficiente. ¡No confirmes código roto al tronco nunca! Sus contribuciones no pueden afectar el trabajo de otros programadores.

Editar

Veo que pretendías pruebas rotas, pero en mi humilde opinión, hay poca diferencia. El objetivo de una prueba es determinar si un aspecto particular de un programa pasa o falla. Si siempre falla y no hace nada, la prueba, en el uso tradicional de las pruebas unitarias, no sirve para nada. Si lo usa para realizar alguna otra métrica que no implica necesariamente una confirmación "fallida" si una de esas pruebas falla, le recomendaría encarecidamente que encuentre otra forma de hacer lo mismo.

De lo contrario, corre el riesgo de que la prueba nunca se tenga en cuenta o si hace que su compilación falle, que sus compañeros programadores ignoren las compilaciones fallidas. Es más importante que los programadores se den cuenta cuando han roto una compilación que realizar una prueba que no ofrece una visión real y que solo puede dar lugar a malas prácticas.

Neil
fuente
1
De hecho, haces un punto con las ramas temáticas. Pero nunca estoy hablando de cometer código roto, solo fallar las pruebas. Por ejemplo, podría ayudar a un proyecto de código abierto creando pruebas para tickets entrantes incluso si no sé cómo solucionarlos. Eso ahorra un poco de tiempo para los mantenedores.
Matthieu Napoli
Si estoy trabajando en el mismo proyecto que usted y carga una prueba fallida, ahora tengo una compilación que tiene una prueba fallida. Podría terminar eliminando su prueba, ya que esa funcionalidad aún no está implementada, o decidir implementar la función y terminar pisoteando su código y perdiendo el tiempo. Si hubiera una cultura de hacer esto, ese tipo de respuesta podría evitarse, pero entonces todos lo estarían haciendo, e incluso cuando todas las pruebas pasen, no todas las mías lo harán. En ese caso, la compilación siempre tendría pruebas fallidas. No veo una ventaja.
Michael Shaw
the build would always have failing tests¡precisamente! ¿Pero es eso algo malo? Nuestra única métrica es "la construcción está rota o no", pero su código podría estar lleno de errores conocidos , por lo que eso realmente no significa nada, excepto que no hay regresión. En un mundo perfecto, cada problema del rastreador tendría una prueba (la reproducción es más fácil que la reparación). Entonces, la ventaja sería ver que 35 pruebas / 70% de todas las pruebas están aprobadas, que Branch-A lo mejora a 40 pruebas (80%) sin regresión, y que Branch-B tiene regresiones. Hoy solo se podría decir que Master y Branch-A están bien y Branch-B está roto.
Matthieu Napoli
@ Matthieu Veo a lo que te refieres. Parece que necesita una categoría especial o algo que diga "oye, si esta prueba está fallando, está bien. Lo sabemos. Pero todavía queremos que se ejecute y si pasa, eso es aún mejor y deberíamos eliminar la categoría especial porque ahora nos importa si se rompe "
Earlz
@Earlz ¡Exactamente! Lo que me pregunto es "¿lo ha hecho alguien, en algún lugar? ¿Y hay herramientas que lo respalden (bibliotecas de CI y pruebas unitarias?" Porque si acabo de clasificar esas pruebas con las herramientas clásicas de CI y pruebas unitarias, la compilación siempre fallar de todos modos y no veré una diferencia entre qué pruebas fallaron, por lo que no será útil: /
Matthieu Napoli
4

Dada una rama maestra con pruebas fallidas, ¿cómo puede estar seguro, sin comparar esa lista con las compilaciones anteriores, de que no ha introducido errores?

Simplemente rastrear el número de pruebas fallidas es insuficiente: puede corregir una prueba y romper otra. Y si está de vacaciones, no estará claro para otros que estén mirando la construcción defectuosa.

Mantenga su rama maestra limpia y verde en todo momento. Trabajar en una rama. Mantenga la sucursal debajo de CI, en un trabajo separado, y tenga pruebas fallidas del contenido de su corazón. Solo no rompas maestro.

Haga que el revisor de la sucursal solo combine su sucursal si pasa todas las pruebas. (Más fuerte: ¡haga que el revisor solo pueda fusionar su sucursal si el resultado de fusionar la sucursal en maestro pasa todas las pruebas!)

Frank Shearar
fuente
2
Simply tracking the number of failing tests is insufficientesa no es la única métrica posible. Por ejemplo: Branch-A improves it to 40 tests (80% passing) with no regression. Sin regresión significa que las pruebas aprobadas anteriormente siempre pasan. En resumen, una prueba podría fallar siempre que nunca haya pasado. Me parece que nos faltan cosas buenas al limitarnos a prohibir las pruebas fallidas en las ramas principales. (por supuesto, requeriría herramientas que funcionen de manera diferente: pruebas unitarias, CI, ...)
Matthieu Napoli
Todavía mantengo mi punto: el master siempre debe ser verde, porque es claro y sin ambigüedades. Por supuesto, fallan las pruebas de marcadores ... en una rama de características. CI puede seguir recordando a la gente los errores pendientes.
Frank Shearar
Creo que lo que Matthieu propone es una definición ligeramente diferente de "verde", que no se desvía de que el maestro siempre sea verde. Para mí no es obvio que no tiene sentido, por supuesto, necesitaría algunas herramientas no completamente triviales para el seguimiento. (¿Necesita revertir un cambio que hizo pasar esa prueba? Mala suerte si eso significa que el estado de repente está rojo ...)
Christopher Creutzig
NUnit tiene el concepto de una prueba ignorada. Esa podría ser una alternativa: no se ejecutan para no fallar, pero aún se informa que se ignoran.
Frank Shearar
2

Hay maneras de resolver sus problemas sin desechar las prácticas bien entendidas y aceptadas sobre la integración continua.

Comenzaré con el problema de cometer una 'prueba rota' que corresponde a un ticket. Una solución es crear una o más pruebas de ruptura que expongan el problema, y ​​luego solucionar el problema , para que puedan fusionarse nuevamente en la línea de código principal. La segunda solución es tener las pruebas rotas, pero use algún tipo de indicador de ignorar para que realmente no se ejecuten y rompan la compilación. Posiblemente agregue un comentario o una anotación especial que haga muy obvio que esta es una prueba incompleta Ticket#N. También adjunte una nota al ticket en sí que se refiera a las pruebas creadas que esperan ser ignoradas y ejecutadas. Esto ayudaría a una persona a arreglar el boleto, pero tampoco sería una señal de alerta para alguien que se encuentre con la prueba.

Y sobre su próximo problema con TDD. TDD se trata de escribir una pequeña prueba y luego escribir una pequeña porción de código para hacer que la prueba pase . Luego siga iterando hasta que tenga un pequeño módulo funcional. Siento que si escribes 4-5 pruebas, entonces te vas de vacaciones, podrías estar haciéndolo mal. Puede emparejar el programa con alguien de manera que uno de ustedes escriba la prueba y el otro el código correspondiente. Sin embargo, no debe usar el repositorio de línea de código principal para compartir este código entre ustedes dos antes de que un módulo completado esté listo para ser confirmado. Como otros sugirieron, una rama compartida resolvería sus problemas allí.

Intentar romper el mantra de integración continua puede conducir a caminos inesperados y aterradores. Por ejemplo, ¿qué significaría la cobertura de código en este tipo de entorno ? ¿Cómo no sentirían los desarrolladores que el sistema tiene muchas " ventanas rotas " ? ¿Cómo podría uno hacer un cambio, ejecutar la prueba y saber si realmente están rompiendo algo nuevo, o son solo las cosas viejas?

c_maker
fuente
No necesita ninguna herramienta para compartir con la persona con la que está programando pares, solo entregue el teclado. Si está usando diferentes computadoras, bueno, no hay nada de malo en eso, simplemente no es "programación de pares".
Christopher Creutzig
1

Creo que su problema fundamental es que está incluyendo RESULTADOS de prueba como parte de la compilación. Mientras que obviamente algunas personas están de acuerdo con usted, otras no. Romper la compilación ocurre cuando no se construye. No cuando no se construye sin errores.

Considere un proyecto importante como Windows o Linux, o incluso algo como Firefox: ¿cree que se envían sin errores? Por supuesto no. Ahora, estos proyectos no están haciendo TDD, pero eso es realmente irrelevante: TDD no cambia dos hechos fundamentales: existen errores, y lleva tiempo solucionarlos. Tiempo que un proyecto (de código abierto o no) simplemente no puede permitirse desperdiciar en errores de baja prioridad. KDE recientemente corrigió un error que tenía más de una década. ¿Cuándo fue la última vez que escuchó a alguien decir "Me alegro de que hayamos esperado una década para enviar nuestro proyecto"?

TDD, en cierto modo, probablemente hace que sea más FÁCIL enviar errores, porque tiene una mejor comprensión de cuál es la falla. Si puede definir con precisión qué causa el error, tiene una base excelente para sopesar el costo de solucionarlo.

Mi recomendación es encontrar un proyecto que no le importe algo de rojo entre el verde.

jmoreno
fuente
1
 > a common best practice is to have all the tests passing (green) at all times.

Prefiero que todas las pruebas no fallen (no rojo).

Con esta definición ligeramente diferente, también puede definir pruebas que son

  • aún no implementado (gris en nunit si hay una NotImplementedException)
  • se sabe que falla = "necesita hacer" marcando / anotando la prueba como ignorada (amarillo)

Si los registra en el repositorio, su construcción continua no está rota y, por lo tanto, es válida.

k3b
fuente
0

Podría considerar dos "conceptos" de compilación de CI diferentes.

  1. Construcciones regulares de CI. La compilación de CI regular debe compilar y ejecutar solo aquellas pruebas para las cuales se ha escrito el código para que se aprueben, de modo que los informes de CI de aprobación / falla de la prueba sean un indicador claro y inequívoco de regresión contra el estado previamente aceptado del código.
  2. Una construcción de CI "futura". Esta compilación compila y ejecuta solo aquellas pruebas para las cuales no se ha escrito ningún código específicamente para hacerlas pasar. Puede haber varias razones para hacerse una prueba de este tipo:

    • Se pueden agregar pruebas para casos de falla específicos desde el rastreador de problemas, para el cual aún no se ha intentado ninguna solución. Es claramente útil tener una prueba codificada y en ejecución para un problema, incluso sin una solución.

    • Se agregaron pruebas para la nueva funcionalidad requerida que aún no se ha implementado.

    • A menudo es más fácil saber cómo probar una falla o característica que saber cómo implementarla o corregirla, y separar los dos pasos mediante la confirmación de la prueba al control de origen puede ser útil para garantizar que no se pierda información.

Al igual que en el CI "estándar", en el régimen de desarrollo regular, el equipo solo miraría los resultados de construcción diarios de la construcción regular.

El equipo también puede vigilar la evolución de los casos de prueba de la compilación de CI "futura", específicamente para ver si los cambios realizados en la CI regular realmente solucionan problemas de la compilación "futura", lo que puede ser una indicación de un importante problema subyacente o mejora del diseño.

Finalmente, si un miembro del equipo tiene tiempo extra en sus manos, podría echar un vistazo para solucionar uno de los problemas del "futuro", y si logran migrarlo a "regular" (mientras actualiza el estado del rastreador de problemas).

Joris Timmermans
fuente
0

Y no creo que sea malo tener pruebas fallidas en su repositorio, es como tener problemas abiertos en su rastreador. Estas son solo cosas que esperan ser reparadas.

El problema no es el fracaso de las pruebas, es un indicador de regresión simple y sin contexto. Aka: como desarrollador, ¿puedo verificar un solo indicador y saber si introduje una regresión o un código de ruptura?

En el momento en que introduce el concepto de fallas "suaves" (está bien, estamos trabajando en ello / aún no implementado / estamos esperando la nueva versión / va a pasar nuevamente una vez que se solucione esta otra compilación), necesita todos los que puedan correr o mirar la prueba para conocer el estado esperado. Lo que en un equipo lo suficientemente grande va a cambiar por hora: su indicador deja de tener sentido. En un contexto más pequeño (prueba de integración privada del equipo, por ejemplo), entonces creo que es como una deuda técnica y está bien, solo necesita ser administrado.

La forma en que la mayoría de las direcciones de herramientas es 'verde / pasando' refleja cuál es el resultado esperado, no es que el código esté funcionando:

  • Fitness tiene el concepto de fracaso esperado.
  • JUnit tiene @Ignored / @Test (expect =)
  • Pepino tiene el estado 'aún no implementado' (o como se llame)

Estos vienen con sus propios problemas (¿cómo se distingue entre 'sí, sabemos que está roto, trabajando en ello' y 'el comportamiento correcto es una excepción'), pero ayudan.

ptyx
fuente
0

Yo uso pruebas omitidas.

En el marco de prueba de unidad particular que uso, puedo generar una excepción SkipTest. La prueba no se ejecuta realmente, y su falla no interrumpirá la compilación. Sin embargo, puedo ver la cantidad de pruebas omitidas y ver si hay trabajo por hacer en esa área.

Winston Ewert
fuente