Hemos llegado al punto en nuestro proyecto en el que tenemos casi mil pruebas y la gente ha dejado de molestarse en ejecutarlas antes de hacer un check-in porque lleva mucho tiempo. En el mejor de los casos, ejecutan las pruebas que son relevantes para el fragmento de código que cambiaron y, en el peor, simplemente lo registran sin probar.
Creo que este problema se debe al hecho de que la solución ha crecido a 120 proyectos (generalmente hacemos proyectos mucho más pequeños y esta es solo la segunda vez que hacemos TDD correctamente) y el tiempo de construcción + prueba ha crecido a aproximadamente dos o tres minutos en las máquinas menores.
¿Cómo reducimos el tiempo de ejecución de las pruebas? ¿Hay técnicas? Fingiendo más? Fingiendo menos? ¿Quizás las pruebas de integración más grandes no deberían ejecutarse automáticamente al ejecutar todas las pruebas?
Editar: como respuesta a varias de las respuestas, ya usamos CI y un servidor de compilación, así es como sé que las pruebas fallan. El problema (en realidad un síntoma) es que seguimos recibiendo mensajes sobre compilaciones fallidas. Ejecutar pruebas parciales es algo que la mayoría de las personas hace pero no todas. y con respecto a las pruebas, en realidad están bastante bien hechas, usan falsificaciones para todo y no hay IO en absoluto.
fuente
Respuestas:
Una posible solución sería mover la parte de prueba de las máquinas de desarrollo a una configuración de integración continua ( Jenkins, por ejemplo) utilizando un software de control de versiones de algún sabor ( git , svn , etc.).
Cuando se tiene que escribir un nuevo código, el desarrollador dado creará una rama para lo que sea que estén haciendo en el repositorio. Todo el trabajo se realizará en esta rama y pueden confirmar sus cambios en la rama en cualquier momento sin estropear la línea principal de código.
Cuando la característica dada, la corrección de errores o cualquier otra cosa en la que estén trabajando se haya completado, esa rama puede fusionarse nuevamente en el tronco (o como prefiera hacerlo) donde se ejecutan todas las pruebas unitarias. Si una prueba falla, la fusión se rechaza y se notifica al desarrollador para que pueda corregir los errores.
También puede hacer que su servidor CI ejecute las pruebas unitarias en cada rama de características a medida que se realizan las confirmaciones. De esta forma, el desarrollador puede hacer algunos cambios, confirmar el código y dejar que el servidor ejecute las pruebas en segundo plano mientras continúan trabajando en cambios adicionales u otros proyectos.
Aquí se puede encontrar una gran guía para una forma de hacer tal configuración (específica de git pero debería funcionar para otros sistemas de control de versiones): http://nvie.com/posts/a-successful-git-branching-model/
fuente
La mayoría de las pruebas unitarias deben tomar menos de 10 milisegundos cada una. Tener 'casi un millar de pruebas' es nada y debería tener tal vez unos pocos segundos para funcionar.
Si no lo están, entonces debe dejar de escribir pruebas de integración altamente acopladas (a menos que eso sea lo que necesita el código) y comenzar a escribir buenas pruebas unitarias (comenzando con código bien desacoplado y el uso adecuado de falsificaciones / simulaciones / apéndices / etc.). Ese acoplamiento afectará la calidad de la prueba y el tiempo que lleva escribirlos también, por lo que no se trata solo de reducir el tiempo de ejecución de la prueba.
fuente
py.test
) hace toneladas de magia en el fondo, y todo es código Python puro ("100x más lento que C "), ejecutar las pruebas de alrededor de 500 en un proyecto mío lleva menos de 6 segundos en una netbook lenta de varios años. Esta cifra es aproximadamente lineal en el número de pruebas; Si bien hay algunos gastos generales de inicio, se amortiza en todas las pruebas, y los gastos generales por prueba son O (1).Hay varios enfoques que he usado para resolver un problema similar:
Además, puede usar las siguientes herramientas para facilitarle la vida y que las pruebas se ejecuten más rápido
pnunit
configuración de CI con varios nodos.fuente
0. Escucha a tus programadores.
Si no están ejecutando las pruebas, significa que perciben que el costo (esperando que se ejecuten las pruebas, lidiando con fallas falsas) es mayor que el valor (detectar errores de inmediato). Disminuya los costos, aumente el valor y las personas ejecutarán las pruebas todo el tiempo.
1. Haga sus pruebas 100% confiables.
Si alguna vez tienes pruebas que fallan con falsos negativos, trata eso de inmediato. Arreglarlos, cambiarlos, eliminarlos, lo que sea necesario para garantizar el 100% de fiabilidad. (Está bien tener un conjunto de pruebas poco confiables pero útiles que se pueden ejecutar por separado, pero el cuerpo principal de las pruebas debe ser confiable).
2. Cambie sus sistemas para garantizar que todas las pruebas pasen todo el tiempo.
Utilice sistemas de integración continua para asegurarse de que solo las confirmaciones pasantes se fusionen en la rama principal / oficial / lanzamiento / lo que sea.
3. Cambie su cultura para valorar el 100% de las pruebas de aprobación.
Enseñe la lección de que una tarea no está "hecha" hasta que el 100% de las pruebas pasen y se haya fusionado en la rama principal / oficial / lanzamiento / lo que sea.
4. Haz las pruebas rápido.
He trabajado en proyectos donde las pruebas toman un segundo, y en proyectos donde toman todo el día. Existe una fuerte correlación entre el tiempo que lleva ejecutar las pruebas y mi productividad.
Cuanto más tiempo tarden en ejecutarse las pruebas, con menos frecuencia las ejecutará. Eso significa que pasará más tiempo sin recibir comentarios sobre los cambios que está realizando. También significa que pasarás más tiempo entre confirmaciones. Comprometerse más a menudo significa pasos más pequeños que son más fáciles de fusionar; confirmar la historia es más fácil de seguir; encontrar un error en la historia es más fácil; retroceder también es más fácil.
Imagine pruebas que se ejecutan tan rápido que no le importa ejecutarlas automáticamente cada vez que compila.
Hacer pruebas rápidas puede ser difícil (eso es lo que pidió el OP, ¡verdad!). El desacoplamiento es clave. Los simulacros / falsificaciones están bien, pero creo que puedes hacerlo mejor refactorizando para que los simulacros / falsificaciones sean innecesarios. Vea el blog de Arlo Belshee, comenzando con http://arlobelshee.com/post/the-no-mocks-book .
5. Haga que las pruebas sean útiles.
Si las pruebas no fallan cuando lo arruinas, ¿cuál es el punto? Enséñenos a sí mismos a escribir pruebas que detecten los errores que probablemente crearán. Esta es una habilidad en sí misma y requerirá mucha atención.
fuente
Un par de minutos está bien para pruebas unitarias. Sin embargo, tenga en cuenta que hay 3 tipos principales de pruebas:
Estos se enumeran en orden de velocidad. Las pruebas unitarias deben ser rápidas. No detectarán todos los errores, pero establecen que el programa es decentemente sano. Las pruebas unitarias deben ejecutarse en 3 minutos o menos o hardware decente. ¿Dices que solo tienes 1000 pruebas unitarias y que demoran 2-3 minutos? Bueno, eso probablemente esté bien.
Cosas para verificar:
Sin embargo, asegúrese de asegurarse de que sus pruebas unitarias y las pruebas de integración estén separadas. Las pruebas de integración siempre serán más lentas.
Asegúrese de que las pruebas unitarias se ejecuten en paralelo. No hay razón para que no lo hagan si son verdaderas pruebas unitarias
Asegúrese de que sus pruebas unitarias estén "libres de dependencia". Nunca deben acceder a una base de datos o al sistema de archivos
Aparte de eso, sus pruebas no suenan tan mal en este momento. Sin embargo, como referencia, uno de mis amigos en un equipo de Microsoft tiene 4.000 pruebas unitarias que se ejecutan en menos de 2 minutos en hardware decente (y es un proyecto complicado). Es posible tener pruebas unitarias rápidas. Eliminar las dependencias (y burlarse de todo lo que sea necesario) es lo principal para obtener velocidad.
fuente
Capacite a sus desarrolladores en el Proceso de software personal (PSP) ayudándoles a comprender y mejorar su rendimiento utilizando más disciplina. Escribir código no tiene nada que ver con golpear con los dedos el teclado y luego presionar un botón de compilación y registro.
PSP solía ser muy popular en el pasado cuando compilar código era un proceso que requería mucho tiempo (horas / días en un mainframe, por lo que todos tenían que compartir el compilador). Pero cuando las estaciones de trabajo personales se volvieron más poderosas, todos llegamos a aceptar el proceso:
Si piensa antes de escribir, y luego de escribir, revise lo que escribió, puede reducir la cantidad de errores antes de ejecutar un conjunto de compilación y prueba. Aprenda a no presionar build 50 veces al día, pero tal vez una o dos veces, entonces importa menos que su tiempo de construcción y prueba tome unos minutos más.
fuente
Una forma posible: dividir su solución. Si una solución tiene 100 proyectos, entonces es bastante inmanejable. El hecho de que dos proyectos (digamos A y B) usen algún código común de otro proyecto (digamos Lib) no significa que tengan que estar en la misma solución.
En cambio, puede crear la solución A con los proyectos A y Lib y también la solución B con los proyectos B y Lib.
fuente
Estoy en una situación similar. Tengo pruebas unitarias que prueban la comunicación con el servidor. Están probando el comportamiento con tiempos de espera, cancelando conexiones, etc. Todo el conjunto de pruebas dura 7 minutos.
7 minutos es un tiempo relativamente corto, pero no es algo que harás antes de cada confirmación.
También tenemos un conjunto de pruebas de IU automatizadas, su tiempo de ejecución es de 2 horas. No es algo que quieras ejecutar todos los días en tu computadora.
¿Entonces lo que hay que hacer?
Lo importante es: todas sus pruebas deben ejecutarse con frecuencia porque es importante encontrar los errores. Sin embargo, no es absolutamente necesario encontrarlos antes de los commits.
fuente
Aunque su descripción del problema no proporciona una visión exhaustiva de la base de código, creo que puedo decir con seguridad que su problema es doble.
Aprende a escribir las pruebas correctas.
Dices que tienes casi mil pruebas y tienes 120 proyectos. Suponiendo que como máximo la mitad de esos proyectos son proyectos de prueba, tiene 1000 pruebas para 60 proyectos de código de producción. Eso te da alrededor de 16-17 pruebas pr. ¡¡¡proyecto!!!
Esa es probablemente la cantidad de pruebas que tendría que cubrir alrededor de 1-2 clases en un sistema de producción. Entonces, a menos que solo tenga 1-2 clases en cada proyecto (en cuyo caso la estructura de su proyecto es demasiado fina) sus pruebas son demasiado grandes, cubren demasiado terreno. Dices que este es el primer proyecto que estás haciendo TDD correctamente. A decir, los números que presenta indican que este no es el caso, no está haciendo propiedad de TDD.
Debe aprender a escribir las pruebas correctas, lo que probablemente significa que necesita aprender a hacer que el código sea comprobable en primer lugar. Si no puede encontrar la experiencia dentro del equipo para hacer eso, sugeriría contratar ayuda desde el exterior, por ejemplo, en forma de uno o dos consultores que ayuden a su equipo durante un período de 2-3 meses a aprender a escribir código comprobable, y pequeños Pruebas unitarias mínimas.
Como comparación, en el proyecto .NET en el que estoy trabajando actualmente, podemos ejecutar aproximadamente 500 pruebas unitarias en menos de 10 segundos (y eso ni siquiera se midió en una máquina de alta especificación). Si esas fueran sus cifras, no tendría miedo de ejecutarlas localmente de vez en cuando.
Aprenda a gestionar la estructura del proyecto.
Ha dividido la solución en 120 proyectos. Según mis estándares, es una cantidad asombrosa de proyectos.
Entonces, si tiene sentido tener esa cantidad de proyectos (lo cual tengo la sensación de que no tiene, pero su pregunta no proporciona suficiente información para hacer un juicio calificado de esto), debe dividir los proyectos en componentes más pequeños que se puede construir, versionar e implementar por separado. Entonces, cuando un desarrollador ejecuta la unidad del conjunto de pruebas, solo necesita ejecutar las pruebas relacionadas con el componente en el que está trabajando actualmente. El servidor de compilación debe asegurarse de verificar que todo se integre correctamente.
Pero dividir un proyecto en múltiples componentes compilados, versionados e implementados por separado requiere, en mi experiencia, un equipo de desarrollo muy maduro, un equipo que es más maduro de lo que tengo la sensación de que su equipo es.
Pero, en cualquier caso, debe hacer algo con respecto a la estructura del proyecto. Divida los proyectos en componentes separados o comience a fusionar proyectos.
Pregúntese si realmente necesita 120 proyectos?
ps Es posible que desee comprobar NCrunch. Es un complemento de Visual Studio que ejecuta su prueba automáticamente en segundo plano.
fuente
Las pruebas JUnit normalmente son rápidas, pero algunas de ellas simplemente deben tomarse un tiempo para ejecutarse.
Por ejemplo, la prueba de la base de datos suele tardar unos minutos en inicializarse y finalizar.
Si tiene cientos de pruebas, incluso si son rápidas, requieren mucho tiempo para ejecutarse debido a su número.
Lo que se puede hacer es:
1) Identificar las pruebas cruciales. Los de las partes más importantes de las bibliotecas y los que tienen más probabilidades de fallar después de los cambios. Solo esas pruebas deben ejecutarse siempre en compilación. Si a menudo se rompe algún código, sus pruebas deberían ser obligatorias incluso si tardan mucho en ejecutarse, por otro lado, si alguna parte del software nunca causó un problema, puede omitir las pruebas de forma segura en cada compilación.
2) Prepare el servidor de integración continua, que ejecutará todas las pruebas en segundo plano. Depende de usted si decide construir cada hora o construir después de cada confirmación (la segunda tiene sentido solo si desea detectar automáticamente qué confirmación ha causado problemas).
fuente
Problemas que he visto:
a) Uso de COI para construir elementos de prueba. 70 segundos -> 7 segundos quitando el contenedor.
b) No burlarse de todas las clases. Mantenga sus pruebas unitarias a un solo elemento. He visto pruebas que divagan en un par de clases. Estas no son pruebas unitarias y es mucho más probable que se rompan.
c) Perfílelos para averiguar qué estaba sucediendo. Descubrí que el constructor estaba construyendo cosas que no necesitaba, así que lo localicé y reduje los tiempos de ejecución.
d) Perfil. tal vez el código no es tan bueno y puede obtener algo de eficiencia de una revisión.
e) Eliminar dependencias. Mantener su ejecutable de prueba pequeño reducirá el tiempo de carga. Use una biblioteca de interfaz y contenedores IOC para ejecutar su solución final, pero sus proyectos de prueba principales solo deben tener la biblioteca de interfaz definida. Esto asegura la separación, asegura que sea más fácil de probar y también hace que su huella de prueba sea más pequeña.
fuente
Siento tu dolor, y me he encontrado con varios lugares donde la velocidad de construcción se puede mejorar mucho. Sin embargo, lo que recomiendo es medir con un detalle granular para determinar dónde tarda más su compilación. Por ejemplo, tengo una compilación con alrededor de 30 proyectos que toma poco más de un minuto en ejecutarse. Sin embargo, eso es solo una parte de la imagen. También sé qué proyectos tardan más en construirse, lo que ayuda a centrar mis esfuerzos.
Cosas que comen tiempo de construcción:
Las bibliotecas simuladas usan código de reflexión o de inyección usando bibliotecas de código de bytes para generar el simulacro por usted. Si bien es muy conveniente, consume tiempo de prueba. Si está generando simulacros dentro de un ciclo en su prueba, puede agregar una cantidad de tiempo medible a las pruebas unitarias.
Hay formas de solucionar los problemas:
Cuando su solución contiene más de 100 proyectos, tiene una combinación de código de biblioteca, pruebas y código de aplicación. Cada una de las bibliotecas puede ser su propia solución con sus pruebas asociadas. Jet Brains Team City es un servidor de compilación de CI que también funciona como un servidor Nuget, y estoy seguro de que no es el único. Eso le brinda la flexibilidad de mover esas bibliotecas que probablemente no cambien a menudo a sus propias soluciones / proyectos y usar Nuget para resolver las dependencias para el código de su aplicación. Las soluciones más pequeñas significan que puede realizar cambios en una biblioteca rápidamente y sin problemas, y disfrutar de los beneficios de la solución principal.
fuente
¿Puede su entorno de prueba ejecutarse en cualquier lugar? Si puede, use la computación en la nube para ejecutar las pruebas. Divida las pruebas entre N máquinas virtuales. Si el tiempo para ejecutar las pruebas en una sola máquina es T1 segundos, entonces el tiempo para ejecutarlas divididas, T2, podría acercarse a T2 = T1 / N. (Suponiendo que cada caso de prueba lleva aproximadamente la misma cantidad de tiempo). Y solo tiene que pagar las máquinas virtuales cuando las usa. Por lo tanto, no tiene un montón de máquinas de prueba en algún laboratorio en algún lugar 24/7. (Me encantaría poder hacer esto donde trabajo, pero estamos vinculados a un hardware específico. No hay máquinas virtuales para mí).
fuente