¿Cuál es el punto de ejecutar pruebas unitarias en un servidor CI?

98

¿Por qué ejecutaría pruebas unitarias en un servidor CI?

Seguramente, para cuando algo se comprometa a dominar, un desarrollador ya ha ejecutado todas las pruebas unitarias antes y ha corregido cualquier error que pueda haber ocurrido con su nuevo código. ¿No es ese el punto de las pruebas unitarias? De lo contrario, acaban de cometer un código roto.

Steve
fuente
51
Nuestros desarrolladores no pueden comprometerse a dominar. Empujan a una rama de características, el servidor de CI luego se fusiona con el maestro y ejecuta pruebas. Si tienen éxito, entonces los cambios se combinan para dominar. Entonces el código con pruebas rotas no puede estar en el maestro ...
Boris the Spider
2
@BoristheSpider: muy buen flujo de trabajo. mastersiempre debe ser sensato y, preferiblemente, implementarse automáticamente en cada fusión en un entorno de preparación para el control de calidad interno y las pruebas.
Según Lundberg el
130
"Seguramente, cuando algo se compromete a dominar, un desarrollador ya ha ejecutado todas las pruebas unitarias antes y ha corregido cualquier error que pudiera haber ocurrido con su nuevo código". ¿En qué mundo de fantasía vives?
jpmc26
55
En algunas industrias, la parte importante no es solo ejecutar las pruebas en el código, es ejecutar las pruebas en los binarios . La ejecución de las pruebas en la salida de CI significa que puede garantizar que el producto entregado funcione, porque el binario exacto que recibió su cliente es el que pasó todas sus pruebas. Suena trivial, pero a veces esto puede tener un efecto (uno que he visto es ofuscación; en proyectos complejos, o cuando se configura de manera extraña, puede causar problemas en la compilación ofuscada que no estaban allí en la versión limpia).
anaximander
55
"Sin duda, en el momento en que algo se ha comprometido a dominar, un desarrollador ha ejecutado ya todas las pruebas unitarias antes y fija los errores que pudiera haber ocurrido con su nuevo código." ... no estoy seguro si seria
chucksmash

Respuestas:

224

Seguramente, para cuando algo se comprometa a dominar, un desarrollador ya ha ejecutado todas las pruebas unitarias antes y ha corregido cualquier error que pueda haber ocurrido con su nuevo código.

O no. Puede haber muchas razones por las cuales esto puede suceder:

  • El desarrollador no tiene la disciplina para hacer eso.
  • Han olvidado
  • No cometieron todo y presionaron un conjunto de confirmación incompleto (gracias Matthieu M.
  • Solo ejecutaron algunas pruebas, pero no toda la suite (gracias nhgrif )
  • Probaron en su rama antes de fusionarse (gracias nhgrif * 2)

Pero el punto real es ejecutar las pruebas en una máquina que no sea la máquina del desarrollador. Uno que está configurado de manera diferente.

Esto ayuda a detectar problemas en los que las pruebas y / o el código dependen de algo específico de un cuadro de desarrollador (configuración, datos, zona horaria, configuración regional, lo que sea).

Otras buenas razones para que las compilaciones de CI ejecuten pruebas:

  • Pruebas en diferentes plataformas que no sean las principales plataformas de desarrollo, lo que puede ser difícil para un desarrollador. (gracias TZHX )
  • Aceptación / Integración / End to End / Las pruebas de ejecución realmente larga pueden ejecutarse en el servidor CI que normalmente no se ejecutaría en un cuadro de desarrollador. (gracias Ixrec )
  • Un desarrollador puede hacer un pequeño cambio antes de presionar / comprometerse (pensando que este es un cambio seguro y, por lo tanto, no ejecuta las pruebas). (gracias Ixrec * 2)
  • La configuración del servidor CI generalmente no incluye todas las herramientas y configuración del desarrollador y, por lo tanto, está más cerca del sistema de producción
  • Los sistemas de CI construyen el proyecto desde cero siempre, lo que significa que las compilaciones son repetibles
  • Un cambio en la biblioteca podría causar problemas en sentido descendente: se puede configurar un servidor CI para construir todas las bases de código dependientes, no solo la biblioteca
Oded
fuente
36
Otras razones comunes: 1) El servidor CI puede ejecutar pruebas de integración / aceptación de alto nivel que demoran demasiado tiempo para que los desarrolladores las ejecuten siempre. 2) El desarrollador los ejecutó y luego realizó un pequeño cambio antes de insistir en que estaban muy seguros de que no romperían nada, pero queremos estar seguros.
Ixrec
11
Un cambio a una dependencia a menudo también ejecuta todas las compilaciones posteriores. Si un cambio que realiza un desarrollador rompe algo aguas abajo, no se ve fácilmente al modificar una biblioteca (por ejemplo, cambiar un tipo de datos subyacente de un SortedSet a un HashSet (solo proporcionando el contrato de Set) y alguien aguas abajo trabajó en la suposición errónea de que el conjunto fue ordenado). No ejecutar las pruebas (posteriores) en el servidor de CI permitiría que el error se infecte por un tiempo.
2
@MichaelT Buena captura. Esa es en realidad la causa de> 90% de nuestras fallas de CI en estos días, no estoy seguro de cómo lo olvidé ...
Ixrec
34
Además, ejecutarlos en un entorno CI generalmente significa que configura su proyecto desde cero , lo que garantiza que su compilación sea repetible .
mgarciaisaia
55
Además, podrían confirmarse dos cambios que se probaron bien por separado, pero se separan (por ejemplo, uno elimina una API no utilizada y el otro comienza a usarla).
Simon Richter
74

Como desarrollador que no ejecuta todas las pruebas de integración y unidad antes de comprometerse con el control de código fuente, ofreceré mi defensa aquí.

Tendría que construir, probar y verificar que una aplicación se ejecute correctamente en:

  • Microsoft Windows XP y Vista con el compilador Visual Studio 2008.
  • Microsoft Windows 7 con el compilador Visual Studio 2010.
    • Ah, y el MSI construye para cada uno de esos.
  • RHEL 5 y 6 con 4.1 y 4.4 respectivamente (de manera similar a CentOS)
    • 7 pronto. Woop-de-woop.
  • Fedora Workstation con GCC para las últimas tres versiones recientes.
  • Debian (y derivados como Ubuntu) para las últimas tres versiones recientes.
  • Mac OSX en las últimas tres versiones recientes.
    • Y los paquetes (rpm, dmg, etc.)

Agregue el Fortran (con los compiladores Intel y GNU), Python (y sus diversas versiones dependiendo del sistema operativo) y los componentes de script bash / bat y, bueno, creo que puede ver cómo las cosas se disparan.

Entonces son dieciséis máquinas que tendría que tener, solo para ejecutar algunas pruebas un par de veces al día. Sería casi un trabajo de tiempo completo solo administrar la infraestructura para eso. Creo que casi todos estarían de acuerdo en que no es razonable, especialmente multiplicándolo por la cantidad de personas en el proyecto. Entonces dejamos que nuestros servidores CI hagan el trabajo.

Las pruebas unitarias no te impiden cometer código roto, te dicen si saben que has roto algo. La gente puede decir "las pruebas unitarias deben ser rápidas", y continuar con los principios y patrones de diseño y metodologías, pero en realidad a veces es mejor dejar que las computadoras que hemos diseñado para tareas repetitivas y monótonas hagan eso y solo se involucren si dinos que han encontrado algo.

TZHX
fuente
3
El código de prueba de pruebas unitarias no es configuraciones. Sería muy inerte de su parte agregar una nueva prueba y tirarla por la pared sin siquiera ejecutarla localmente primero ...
Robbie Dee
33
@RobbieDee ¿Me temo que no puedo ver tu punto? No sugiero la creación de nuevas pruebas sin la prueba de forma local, o simplemente ciegamente cometer cosas a control de código fuente sin probar por sí mismo, y me gustaría ejecutar las pruebas en mi propia máquina - pero ¿necesito "configuración" a hacerse la prueba de comportamiento coherente , y es mejor hacerlo relativamente rápido cuando la mente del desarrollador todavía está en esa área que encontrar un problema cuando el equipo que usa Macs predominantemente se despierta a cuatro mil millas de distancia y actualiza sus copias.
TZHX
77
@RobbieDee Diría que TZHX ejecutaría todas las pruebas localmente si pudieran hacerlo, pero no pueden . Dado que TZHX no puede, ejecutan algunas pruebas localmente (aquellas que pueden ejecutarse en su sistema de desarrollo y lo suficientemente cortas o más relevantes para el código modificado, por ejemplo), y dejan que la batería completa se ejecute en el sistema CI. Bastante razonable
muru
11
@RobbieDee: Él cree en las pruebas unitarias. Entonces los prueba en su Macbook air y pasa y se registra. Los servidores CI que ejecutan Red Hat, Solaris y Windows vuelven a ejecutar esas pruebas. ¿No es bueno saber que lo que probaste también funciona en plataformas de producción?
slebetman
2
@RobbieDee: a menudo escribí pruebas unitarias que eran específicas de un determinado compilador en una determinada plataforma. Considere, por ejemplo, un subsistema de gráficos que utiliza las instrucciones específicas de CPU de AMD (el competidor de Intel) que solo están disponibles en g ++ (el compilador GNU C ++) versión 4.5 o posterior, pero resulta que trabajo en una CPU Atom e ICC (Intel C ++ Compilador). Sería una tontería ejecutar las pruebas AMD / g ++ 4.5 cada vez en esa máquina, sin embargo, es un código que se probará antes del lanzamiento; Además, mi propio código independiente de la CPU debe probarse para una interoperabilidad adecuada. Claro, hay máquinas virtuales y emuladores, ...
phresnel
23

Aparte de la excelente respuesta de Oded:

  • Usted prueba el código del repositorio . Puede funcionar en su máquina con sus archivos ... que olvidó comprometer. Puede depender de una nueva tabla que no tenga el script de creación (en liquibase, por ejemplo), algunos datos de configuración o archivos de propiedades.
  • Evita problemas de integración de código. Un desarrollador descarga la última versión, crea pruebas de unidad e integración, agrega código, pasa todas las pruebas en su máquina, se compromete y empuja. Otro desarrollador acaba de hacer lo mismo. Ambos cambios son correctos por sí solos, pero cuando se combinan provoca un error. Esta podría ser la fusión del repositorio o simplemente que no se detecta como un conflicto. Por ejemplo, Dev 1 elimina el archivo que no se utilizó en absoluto. Dev 2 codifica contra este archivo y prueba sin Dev 1 cambios.
  • Desarrolla una secuencia de comandos para implementar automáticamente desde el repositorio. Tener un script de construcción y despliegue universal resuelve muchos problemas. Algunos desarrolladores pueden haber agregado una opción de compilación o lib que no todos comparten. Esto no solo le ahorra tiempo, sino que lo que es más importante, hace que la implementación sea segura y predecible. Además, puede volver en su repositorio a la versión 2.3.1 e implementar esta versión con un script que funcione con esta versión. Incluye objetos de base de datos como vistas, procedimientos almacenados, vistas y desencadenantes que deben versionarse. (O no podrá volver a una versión viable).
  • Otras pruebas : como integración, rendimiento y pruebas de extremo a extremo. Esto puede ser lento y puede incluir herramientas de prueba como Selenium. Es posible que necesite un conjunto completo de datos con una base de datos real en lugar de objetos simulados o HSQL.

Una vez trabajé en una empresa que tenía muchos errores en la implementación debido al proceso de fusión e implementación. Esto fue causado por un extraño marco propietario que dificultó las pruebas y el CI. No fue una experiencia feliz encontrar que el código que funcionó perfectamente en el desarrollo no llegó directamente a la producción.

Borjab
fuente
Yeap, simplemente olvidarse de cometer algunos de los cambios es muy común. Yo diría que olvidarse de "svn add" nuevos archivos y, por lo tanto, olvidarse de confirmarlos más tarde es la forma más popular de obtener una compilación automática fallida.
Sharptooth
22

Uno pensaría que no, pero los desarrolladores son humanos y a veces se olvidan.

Además, los desarrolladores a menudo no pueden extraer el último código. Sus últimas pruebas podrían funcionar bien, luego, en el momento del check-in, alguien más comete un cambio importante.

Sus pruebas también pueden depender de un recurso local (sin marcar). Algo que las pruebas de tu unidad local no detectarían.

Si cree que todo lo anterior es fantástico, hay un nivel por encima de CI (al menos en TFS) llamado Gated donde las construcciones que tienen pruebas fallidas se archivan y no se comprometen con la base del código.

Robbie Dee
fuente
77
He visto más oops. Olvidé cometer las fallas de CI que me gustaría admitir.
Dan Neely
@DanNeely Para ser justos, es mejor que el gerente de construcción te patee el trasero porque olvidaste decirle algo sobre él ... :-)
Robbie Dee
3
Esa es una de las razones por las que amo a CI. Encontrar y arreglar tus propios ooopses es mucho mejor que hacer que alguien más los encuentre por ti.
Dan Neely
14

para cuando algo se compromete a dominar

Normalmente configuro mi CI para que se ejecute en cada confirmación. Las ramas no se fusionan en master hasta que la rama ha sido probada. Si confía en ejecutar pruebas en el maestro, eso abre una ventana para que se rompa la compilación.

La ejecución de las pruebas en una máquina CI tiene que ver con resultados reproducibles. Debido a que el servidor CI tiene un entorno limpio conocido extraído de su VCS, sabe que los resultados de la prueba son correctos. Cuando se ejecuta localmente, puede olvidar confirmar el código necesario para que lo aprueben, o tener un código no confirmado que los haga pasar cuando deberían fallar.

También puede ahorrar tiempo a los desarrolladores al ejecutar diferentes suites en paralelo, especialmente si algunas son pruebas lentas de varios minutos que probablemente no se ejecutarán localmente después de cada cambio.

En mi trabajo actual, nuestra implementación de producción está cerrada cuando CI pasa todas las pruebas. Los scripts de implementación evitarán la implementación a menos que estén pasando. Esto hace que sea imposible olvidar accidentalmente ejecutarlos.

El hecho de que CI sea parte del flujo de trabajo también quita la carga de los desarrolladores. Como desarrollador, ¿usualmente ejecuta un linter, un analizador estático, una prueba unitaria, una cobertura de código y una prueba de integración para cada cambio? CI puede, de forma completamente automática y sin necesidad de pensarlo, reduciendo la fatiga de la decisión.

Daenyth
fuente
1
Realmente no deberías tener pruebas unitarias lentas, esto viola los principios de FIRST .
Robbie Dee
44
@RobbieDee: Creo que generalmente el servidor CI ejecuta todas las pruebas, no solo las pruebas unitarias.
RemcoGerlich
44
@RobbieDee: en teoría, todas las pruebas unitarias son rápidas. En la práctica ... Independientemente, CI puede y debe ejecutar todas las pruebas: linters, análisis estático, pruebas unitarias, pruebas de integración.
Daenyth
2
@RobbieDee Obviamente, los detalles de la configuración variarán de un equipo a otro. Incluso cuando las compilaciones toman varios minutos, a menudo es posible ejecutar varias de esas compilaciones en paralelo. Dada una sola base de código monolítico, podría ser un inconveniente mayor, pero IME no es una barrera.
Daenyth
1
@RobbieDee Creo que depende más de tu arquitectura. Lo he visto funcionar a mano para un equipo de ingeniería de ~ 80, pero eso es con sub-equipos bien definidos para áreas de productos.
Daenyth
4

Para cuando algo se compromete a dominar, un desarrollador ya debería haber ejecutado todas las pruebas unitarias ... pero ¿y si no lo han hecho? Si no ejecuta las pruebas unitarias en el servidor CI, no lo sabrá hasta que otra persona extraiga los cambios en su máquina y descubra que las pruebas simplemente se rompieron en ellos.

Además, el desarrollador puede haber cometido un error y hacer referencia a un recurso local específico para su máquina. Cuando registran el código y falla la ejecución de CI, el problema se identifica de inmediato y se puede corregir.

David Arno
fuente
3

Suponiendo (al contrario de otras respuestas) que los desarrolladores son bastante disciplinados y ejecutan pruebas unitarias antes de comprometerse, puede haber varias razones:

  • ejecutar pruebas unitarias puede llevar mucho tiempo para una configuración especial. Por ejemplo, ejecutar pruebas unitarias con el comprobador de memoria (como valgrind) puede llevar mucho más tiempo. Aunque todas las pruebas unitarias están pasando, la verificación de memoria puede fallar.
  • el resultado no es tan importante para algunas configuraciones especiales; por ejemplo, ejecutar pruebas unitarias para verificar la cobertura del código requiere indicadores de compilación especiales. Para los desarrolladores normales, la cobertura del código no es tan importante: es más para las personas que se preocupan de que el código mantenga cierta calidad, como los líderes del equipo.
BЈовић
fuente
3

Es posible imaginar casos en los que el cambio A no interrumpe la prueba y el cambio B no interrumpe la prueba, pero A y B juntos lo hacen. Si A y B están hechos por diferentes desarrolladores, solo el servidor CI detectará el nuevo error. A y B pueden ser incluso dos partes de la misma oración más larga.

Imagine un tren conducido por las dos locomotoras A y B. Quizás uno sea más que suficiente y esta sea la solución para aplicar. Sin embargo, si se aplican las dos "soluciones" eliminando ambas, el tren no se moverá.

Además, no todos los desarrolladores ejecutan todas las pruebas de Unidad, mientras que la mayoría de los buenos desarrolladores lo hacen.

h22
fuente
2

Hagamos una pregunta equivalente:

¿Por qué construiría el código en un servidor CI?

Seguramente, para cuando algo se comprometa a dominar, un desarrollador ya ha creado el código antes y reparó los errores que podrían haber ocurrido con su nuevo código. ¿No es ese el punto de construir código? De lo contrario, acaban de cometer un código roto.


Hay varias razones para hacer CI, pero el punto principal de CI es tener una idea de cuál es el estado del código con el tiempo. El principal beneficio (de varios) que proporciona esto es que podemos averiguar cuándo se rompe la compilación, averiguar qué lo rompió y luego solucionarlo.

Si el código nunca se rompe, ¿por qué usamos CI? Para entregar compilaciones para pruebas, las compilaciones nocturnas serían lo suficientemente buenas.

Peter
fuente