CommunityProgramación y desarrollogithub.com

devseunggwan/aceleración-suite-pruebas

Hacer que un conjunto de pruebas lento o un trabajo de CI sea más rápido y demostrarlo: clasificar dónde se va el tiempo en CI en múltiples ejecuciones, preferir correcciones estructurales (configuración compartida, archivos paralelos, divisiones en matriz) sobre reescrituras por archivo, verificar la seguridad del paralelismo y aceptar el cambio solo con una comparación medida de CI antes/después.

¿Qué es aceleración-suite-pruebas?

Esta habilidad para agentes de IA aborda el problema común de un conjunto de pruebas lento en CI, evitando soluciones superficiales como reescribir el archivo más lento o reportar tiempos locales. El flujo de trabajo comienza estableciendo una línea base en CI (nunca local), clasificando los costos de los pasos y estimando las ganancias esperadas antes de elegir una palanca. Se priorizan las soluciones estructurales (configuración compartida, ejecución en paralelo, división en matriz de CI) sobre las reescrituras por archivo, y se requiere una verificación rigurosa de la seguridad del paralelismo. La aceptación final del cambio se basa únicamente en una tabla de comparación de CI antes/después, no en temporizaciones locales. La habilidad incluye referencias detalladas para medir CI, clasificar causas de lentitud en pytest y vitest, configurar ejecutores paralelos y eliminar ejecuciones redundantes de CI. También proporciona scripts para recopilar métricas de duración de CI y analizar costos de llamadas a servicios externos. El proceso se divide en pasos claros (1 a 8) que guían al agente desde la medición de la línea base hasta la elaboración de un informe final, asegurando que cada cambio esté respaldado por datos medibles y pruebas de no regresión.

Compatible con~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/devseunggwan/praxis/tree/HEAD/skills/test-suite-speedup

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

test-suite-speedup

Resumen

Un conjunto de pruebas lento tienta a dos movimientos equivocados: reescribir a mano el archivo de aspecto más lento y reportar un cronómetro local como la ganancia. Ambos fallan. El tiempo suele estar en algunos lugares estructurales: configuración pesada repetida, archivos que se ejecutan en serie cuando podrían ejecutarse en paralelo, un trabajo de CI que hace el trabajo de dos trabajos, y una medición local tomada mientras se ejecuta cualquier otra cosa es ruido. Esta habilidad primero clasifica el costo en CI, elige la palanca que mueve más archivos y trata la tabla de antes/después de CI como la prueba de aceptación.

Principio fundamental: el entregable es una comparación medida de CI en la misma ruta de CI, no una medición local ni verificaciones en verde.

Mapa de referencia: cargue solo cuando el paso se ejecute:

ArchivoContiene
references/ci-measurement.mdPasos de clasificación en múltiples ejecuciones, clasificación por archivo a partir de un registro pytest -v, ruido del tipo de ejecutor, informe de antes/después, actualización de fragmentos de duración
references/pytest-slow-test-causes.mdClasificación de un archivo pytest lento por fase y firma; sleeps escapados; esperas inalcanzables de código de producto; parches herméticos de todo el conjunto (clientes en la nube, sleeps inactivos de bibliotecas) con pruebas de guardia; perfilado de un solo proceso de un flujo de trabajo; reducción de un create_all en tiempo de ejecución
references/pytest-shared-setup.mdDimensionamiento de la parte compartible de una fábrica activa a partir de sus llamadores; compartir un objeto pytest costoso de forma segura: pruebas antes y después, cableado de cambio mínimo, interacción de selección de cambios
references/vitest-isolation.mdConjuntos de pruebas JS/vitest: clasificación de archivos espera-vs-cómputo, archivos lentos solo en CI, costo dominado por importaciones, perfilado de importaciones por módulo, preempaquetado de bibliotecas barrel, grupos aislados vs compartidos, sondas de fugas, verificaciones de barajado
references/parallel-runner.mdSonda de seguridad de paralelismo, partición en serie, reglas de implementación del ejecutor, división de matriz de CI
references/ci-redundant-runs.mdEjecuciones que no deberían ocurrir: clasificación del historial de backmerge/push por árbol, reutilización del resultado de un árbol idéntico, sustitución por una ejecución nueva real (API de Actions, no análisis de mensajes), concurrencia por trabajo, prueba de las puertas if: del flujo de trabajo como código, mantener los marcadores de salto fuera del texto de confirmación
scripts/ci_leg_series.shSegundos de paso de prueba por rama para cada ejecución de push desde una fecha (TSV): la serie para localizar cambios de paso; ejecutar en segundo plano
scripts/ci_durations_diff.pyPara pares de ejecuciones antes/después alrededor de cada cambio de paso: recuentos recopilados, resúmenes de pytest, entradas nuevas o aumentadas de --durations
scripts/boto_call_counter.pyPlugin de pytest que cuenta llamadas reales de botocore por prueba: ejecútelo cuando cProfile muestre esperas SSL/socket debajo de botocore

Cuándo Usar

  • Un conjunto de pruebas, o el trabajo de prueba de un flujo de trabajo de CI, es demasiado lento y el usuario quiere que sea más rápido.
  • El usuario pregunta "¿qué más es lento?" después de una aceleración anterior.
  • No para limpieza de copias/duplicación sin un objetivo de tiempo: reestructurar pruebas para su reutilización no es una aceleración (consulte test-suite-consolidation).

Proceso

Paso 1: Línea base en la misma ruta de CI

Tome la línea base de CI, nunca de una ejecución local. La línea base debe ejercitar la misma ruta de CI que la ejecución posterior: cuando la base del PR selecciona la ruta (por ejemplo, prod ejecuta el conjunto completo dividido en ramas de matriz mientras que dev ejecuta una rama con selección de cambios), tome la última ejecución exitosa de un PR en la misma base y verifique que los recuentos de pasados/saltados/deseleccionados coincidan antes de comparar los tiempos. Obtenga los tiempos por paso para esa ejecución, divida el paso grande por los banners de sección del ejecutor y lea los comentarios del archivo de flujo de trabajo (los mantenedores a menudo ya escribieron la solución prevista). Luego clasifique los pasos en múltiples ejecuciones, no solo una, y verifique el recuento real de vCPU del ejecutor contra el recuento de trabajadores configurado. Comandos → references/ci-measurement.md.

Antes de proponer algo, busque en issues y PRs un problema de aceleración existente, incluidos los cerrados y los PRs de experimentos desechables (gh pr list --state all --search "xdist", --search "test_durations"): registran alternativas medidas y rechazadas, y las condiciones bajo las cuales fueron rechazadas (una palanca refutada cuando dominaba el costo de configuración puede dar frutos una vez que las pruebas con límite de CPU lo hacen).

Hecho cuando: se nombra un ID de ejecución de línea base en la ruta de CI coincidente, se registran sus recuentos de pruebas, y cada paso de más de unos segundos tiene una mediana y p90 en múltiples ejecuciones.

Paso 2: Dimensionar la ganancia esperada antes de elegir una palanca

Ganancia ≈ (proporción del tiempo del conjunto que cubre el lote) × (ahorro por archivo medido en ese subconjunto). Un lote que es el 2% del conjunto da aproximadamente 1–2% incluso con un −60% por archivo: por debajo del ruido de CI, y el usuario lo leerá como "apenas se movió". Prefiera palancas que muevan cientos de archivos a la vez (un helper de prueba compartido, un clasificador de grupos, el tipo de ejecutor) y presente las migraciones por archivo como la cola larga. Diga la ganancia esperada de antemano.

Hecho cuando: cada palanca candidata tiene una estimación por escrito con sus entradas, y las estimaciones por debajo del ruido de CI están etiquetadas como tales.

Paso 3: Clasificar archivos y clasificar cada archivo superior

Clasifique los archivos de todo el conjunto a partir de una ejecución de CI de conjunto completo que registre pytest -v (una ejecución de selección de cambios no cubre todas las pruebas). Valide la clasificación en una ejecución con una respuesta conocida primero: la ejecución previa a la corrección debe clasificar primero el archivo que corrigió. Luego clasifique cada archivo superior por fase (setup / call / teardown) y forma antes de proponer una corrección:

Los tiempos locales por archivo son solo para la forma: ejecute cada archivo en segundo plano, mire por qué es lento (un proceso generado por caso, sleeps reales: un sleep dentro de la cadena de entrada de una prueba no es un retraso) y nunca reporte un número tomado mientras se ejecuta otro conjunto. Registre la CPU (user+sys) junto con el tiempo de pared para cada archivo. Un archivo que usa poca CPU para su tiempo de pared está esperando tiempo real (limitadores, retroceso, sondeo) y la solución es tiempo falso del lado de la prueba; más trabajadores o reducciones de importación no lo ayudarán. Un archivo que es lento en CI pero rápido localmente bajo el recuento de trabajadores y el entorno de CI necesita una reproducción en el sistema operativo de CI y el entorno de ejecución principal (un contenedor node:<major> limitado a la vCPU de CI), no una suposición; lea las líneas de prueba lenta que el registro de CI ya imprime debajo de ese archivo primero → references/vitest-isolation.md. Para pytest, compare la proporción CI/local por archivo contra la proporción habitual del conjunto. Un archivo varias veces por encima tiene un costo solo de CI, pero su causa es una hipótesis hasta que la ejecución de CI del propio PR mueva ese archivo: una comparación A/B de contenedor Linux (por ejemplo, sqlite synchronous=OFF reduciendo las simulaciones del programador ~35%) no se trasladó al ejecutor de GitHub, donde las mismas pruebas mantuvieron sus --durations. Envíe dicha palanca solo junto con un objetivo medible en la primera ejecución del PR → references/pytest-slow-test-causes.md.

La causa de un conjunto lento suele ser una combinación: trabajadores en serie, archivos en espera e importaciones pesadas por archivo. Clasifique las palancas en una tabla: causa · evidencia (medida vs estimada) · segundos de CI · corrección · ahorro esperado.

Una causa adivinada a partir de los nombres de --durations ("probablemente vuelve a analizar el DAG") es una hipótesis hasta que una ejecución de cProfile cumtime de 2–3 de las pruebas lentas lo muestre; perfile antes de que la causa vaya a un plan o un issue, y retírela en el informe cuando el perfil diga lo contrario.

Hecho cuando: cada archivo de clasificación superior tiene una causa de la tabla de clasificación, o está marcado explícitamente como "necesita una revisión".

Paso 4: Elegir la corrección estructural, en este orden

  1. Eliminar la configuración costosa repetida dentro de un archivo activo (una prueba que reconstruye el mismo objeto pesado por llamada: una bolsa DAG, una aplicación, un esquema). Reutilice el fixture de sesión existente del repositorio antes de inventar un caché, y demuestre que compartir es seguro primero, luego demuestre que las copias base y rama del archivo fallan en los mismos mutantes de producción. Un caché de módulo/sesión solo desduplica dentro de un proceso de pytest: en una CI fragmentada, verifique en qué rama se ejecuta cada consumidor antes de contar el ahorro. Cuando una prueba separada solo relee el resultado costoso de un caso parametrizado, fusione sus afirmaciones en ese caso en lugar de almacenar en caché (independiente del fragmento), y rompa una afirmación movida para demostrar que aún se ejecuta → references/pytest-shared-setup.md.
  2. Ejecutar archivos de prueba independientes en paralelo dentro del punto de entrada de CI.
  3. Dividir el trabajo de CI en ramas de matriz para que las mitades se ejecuten en paralelo.
  4. Solo entonces reescribir las generaciones por caso de los archivos más lentos: mucho cambio, ganancia modesta. Ofrézcalo; no lo haga por defecto.

Cuando la mayor parte del costo son ejecuciones completas que no necesitan ocurrir (backmerges de bot, un árbol idéntico ya probado en otra rama, pushes a punto de ser superados), esa palanca viene antes de todo lo anterior: clasifique el historial de ejecuciones por árbol y salte o reutilice ejecuciones completas → references/ci-redundant-runs.md.

Hecho cuando: la palanca elegida se nombra con su estimación del Paso 2, y cualquier palanca de orden superior saltada tiene una razón de una línea.

Paso 5: Probar la seguridad del paralelismo e implementar el cambio de ejecutor

Ejecute cada archivo en paralelo bajo un directorio de estado desechable, cada uno en su propio archivo de salida y código de salida, enumere los fallos y vuelva a ejecutar cada fallo solo para separar un error de concurrencia del entorno. Divida los archivos que editan el árbol de trabajo en un grupo en serie con clave en un marcador que ya llevan. Luego implemente el ejecutor y, si se elige, la división de matriz de CI. Procedimiento y reglas → references/parallel-runner.md.

Hecho cuando: cada fallo de la sonda de paralelismo está clasificado (concurrencia / entorno / preexistente), el grupo en serie se deriva de un grep en lugar de una lista manual, y las comprobaciones de estado requeridas se han verificado antes de cualquier cambio de nombre de trabajo.

Paso 6: Verificar en CI, no sentándose en ejecuciones locales

Mantenga el código de prueba modificado según la guía de estilo del propio repositorio como el código de producto: sin envoltorios de paso, duplicación extraída, una prueba de mutación para cualquier helper nuevo. Ejecute localmente solo los archivos modificados más el lint, haga push y observe las comprobaciones del PR en segundo plano en lugar de mantener la espera. Lea los hilos de revisión del PR antes de escribir cualquier nota de verificación: los revisores bot publican en minutos después de un push.

Cuando una prueba falla solo en un entorno local (un venv de dependencia opcional, un servicio falso local), ejecute esa prueba en la base no modificada en un árbol de trabajo desechable separado antes de tocar el cambio: el mismo fallo allí es entorno, no regresión. Menciónelo en el tráiler Not-tested: del commit y en el cuerpo del PR, y ciérrelo desde el trabajo de CI que instala esa dependencia (su registro sin formato: la línea de resumen más grep -c FAILED = 0), no desde la ejecución local.

Una comparación local de conjunto completo base-vs-rama respalda "sin nuevos fallos" solo en la medida en que se reprodujo: cuando los fallos solo de rama comparten un error de entorno, diga "N fallos solo de rama, todos ; reproducido en base" y deje que la ejecución de CI cierre el resto: "0 nuevos fallos" lo exagera y debe corregirse en cada superficie que alcance.

Antes de llamar a un stub de todo el conjunto seguro para fusionar, enumere cómo podría cambiar los resultados, cada uno con una sonda: (1) enlaces a nivel de módulo que lo evitan (git grep -nE "from <mod> import <fn>" — un nombre vinculado en la importación mantiene la función real); (2) llamadores de producción que no capturan la excepción del stub, y si alguna prueba los ejecuta sin parchear (los recuentos de llamadas por prueba de la ejecución anterior lo muestran); (3) pruebas que afirman la ruta real: deben parchearla ellas mismas, anidadas dentro del stub de sesión; (4) una actualización de biblioteca en curso que mueve el nombre parcheado (consulte la regla tolerante a versiones en pytest-slow-test-causes.md). Informe cada uno como verificado o como una inferencia con lo que lo confirmaría.

Los revisores bot pueden ejecutarse más tiempo que una encuesta corta: el conector Codex marca un PR 👀 mientras revisa y lo cambia por 👍 cuando termina sin hallazgos, a veces más de 20 minutos. Limite la espera, luego vuelva a leer las reacciones y los hilos una vez antes de la reunión informativa de fusión en lugar de extender la encuesta. Para saber si un 👍 en pie pertenece al último push, lea su marca de tiempo (gh api graphql → pullRequest{reactions(first:20){nodes{content createdAt user{login}}}}) contra la hora del push: un 👍 más antiguo que el push es el veredicto de la ronda anterior.

Una ejecución pull_request prueba la fusión de la cabeza en la punta base en ese momento, no el commit base del propio PR: lea el sha base del registro del trabajo (HEAD is now at <m> Merge <head> into <base>) y compárelo con la ejecución de push de ese commit base.

La métrica por archivo es la medición posterior cuando el conjunto creció entre la línea base y la ejecución del PR: los segundos de toda la rama entonces se mezclan con las pruebas de otros commits (compare primero los recuentos de collected N items). Informe los segundos de CI del archivo modificado de ambas ejecuciones como resultado y los tiempos de la rama como contexto. Después de fusionar la base en el PR, reconstruya la banda de comparación a partir de las ejecuciones de otros PRs en el mismo período, no la banda anterior: el conjunto de la propia base sigue creciendo, por lo que la banda anterior puede hacer que una ganancia real parezca una regresión. Diga en el informe cuándo se movió la banda.

Hecho cuando: una ejecución de CI de la rama en la misma ruta que la línea base ha terminado, y cada hallazgo de revisión abierto está respondido.

Paso 7: Reportar el antes/después

Una tabla con ambos IDs de ejecución: tiempo de pared del trabajo (ahora = la rama más lenta), el paso antes lento y la mitad sin cambios, más la línea de resumen de prueba por rama. Para una ganancia repartida en varios PRs, una tabla: línea base → cada paso fusionado → ahora, con la parte que cada PR contribuyó. Nombra la ruta de CI que el PR no ejerció como no verificada, diga que es una ejecución cada una (varianza del ejecutor no medible) y pida al usuario que la ejecute contra la rama de destino cuando la fusionen. Termine con "Próximo candidato:" y la siguiente palanca del Paso 2 que superó el ruido.

El informe es el commit body del squash-merge del PR, más el comentario de PR. Muestre los errores de CI que detuvo la serie en el informe de la misma manera que el paso, no como una nota al pie: "El paso X falló una ejecución antes del cambio (enlace) y 0 después" es la medida, no "fallo corregido incidentalmente".

Hecho cuando: la tabla está en el cuerpo del commit final y en un comentario del PR, y el paso que detiene la serie está medido.

Paso 8: Confirmar la paridad de mutaciones de cualquier fixture compartido

Cuando el Paso 4 eligió compartir un fixture pesado, confirme que la base y la rama fallan en los mismos mutantes de producción: ejecute la prueba de mutación en el archivo modificado y en las pruebas que lo usan, y verifique que los mutantes eliminados por las pruebas coincidan. Una coincidencia exacta confirmaría que compartir no perdió cobertura; una coincidencia parcial (todos los mutantes base también eliminados por la rama, más algunos nuevos) muestra un cambio en la cobertura; una falta de coincidencia muestra cobertura perdida. Procedimiento y reglas → references/pytest-shared-setup.md.

Hecho cuando: la tabla de superposición de mutaciones está en el informe, y las lagunas están justificadas o arregladas.

Hacer y No Hacer

  • Hacer: tomar la línea base de una ejecución de CI en la misma ruta que la corrección.
  • No hacer: ejecutar la suite localmente y reportarlo como ganancia.
  • Hacer: clasificar pasos en múltiples ejecuciones para obtener una mediana y un percentil.
  • No hacer: citar el tiempo de un solo paso de una ejecución.
  • Hacer: medir la CPU junto con el tiempo de pared por archivo.
  • No hacer: mover un fixture sin una prueba de mutación que demuestre que la cobertura sobrevive.
  • Hacer: antes de proponer cualquier solución, busque en issues y PRs experimentos desechables que ya probaron y rechazaron una palanca bajo las mismas condiciones.
  • No hacer: enviar una corrección solo de Linux sin ejecutarla en un ejecutor de GitHub bajo la vCPU y el SO de CI para confirmar que el ahorro se mantiene.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills relacionados