Reading Time: 13 minutes

La integración continua (CI) crea, prueba y valida automáticamente el código de investigación en cada confirmación. Para el software científico, CI es esencial para la reproducibilidad, la detección temprana de errores y el mantenimiento de la calidad a lo largo del tiempo. Implementar CI: (1) Escribir pruebas automatizadas con PyTest, (2) configurar una canalización de CI utilizando GitHub Actions o GitLab CI, (3) usar Docker/Conda para la coherencia del entorno, (4) agregar informes de cobertura y (5) incorporar puntos de referencia de rendimiento. Maneje las pruebas numéricas con pytest.approx, use estrategias de matriz para probar en versiones de Python y dependencias de caché para reducir el tiempo de ejecución. CI transforma el código de investigación de scripts frágiles en software confiable y mantenible.

Introducción: ¿Por qué la integración continua es importante para la investigación?

El software de investigación es conocido por romper en silencio. Un pequeño cambio en una parte del código puede producir resultados sutilmente diferentes aguas abajo, invalidando los hallazgos publicados o perdiendo meses de tiempo de cálculo. Las pruebas manuales tradicionales, ejecutando algunos ejemplos a mano, no se escalan a códigos de simulación complejos con docenas de módulos interdependientes.

La integración continua (CI) soluciona esto ejecutando automáticamente un conjunto de pruebas completo cada vez que se confirma el código. Pero CI es más que solo automatización; Es una disciplina de calidad que hace cumplir la reproducibilidad y valida la corrección continuamente. Como Mejores prácticas de computación científica Notas en papel, «Automated Las pruebas no son negociables para un software científico de confianza» (Wilson et al., 2012).

Para los equipos de investigación, CI ofrece beneficios concretos:

  • Reproducibilidad: CI verifica que el código produce resultados consistentes en entornos y en el tiempo.
  • Detección temprana de defectos: los errores se detectan minutos después de que se introducen, no semanas después durante la preparación del manuscrito.
  • Confianza para refactor: Con una red de seguridad de pruebas, puedes mejorar la estructura del código sin temor a romper algo.
  • Habilitación de colaboración: varios contribuyentes pueden trabajar en la misma base de código con comprobaciones automatizadas que evitan las regresiones.
  • Documentación de las expectativas: las pruebas sirven como especificaciones ejecutables que documentan cómo debe comportarse el código.

A pesar de estos beneficios, muchos proyectos de investigación aún carecen de IC. Las excusas comunes incluyen «nuestro código es demasiado complejo para probar», «las pruebas tardan demasiado» o «no tenemos tiempo para configurar CI». Esta guía desmantela estas objeciones y proporciona un enfoque práctico y paso a paso para CI adaptado al software científico.

¿Qué es la integración continua, realmente?

La integración continua es la práctica de fusionar los cambios de código en un repositorio compartido con frecuencia, idealmente varias veces al día, y verificar automáticamente cada combinación con una compilación y una canalización de prueba automatizadas. La parte «continua» significa que la retroalimentación es rápida; Los desarrolladores saben en cuestión de minutos si su cambio rompió algo.

Una canalización de CI generalmente incluye:

  1. checkout: el sistema CI obtiene el código más reciente.
  2. Configuración de entorno: Las dependencias se instalan (a menudo dentro de un contenedor).
  3. Análisis estático: el código está enlazado por problemas de estilo y posibles errores.
  4. Pruebas unitarias: Las funciones y los módulos individuales se prueban de forma aislada.
  5. Pruebas de integración: se prueban varios componentes juntos.
  6. Informes de cobertura: se mide la fracción de código ejercido por las pruebas.
  7. Edificio de artefacto: se generan documentación, paquetes o binarios.
  8. Benchmarks de rendimiento (opcional): se realiza un seguimiento de la velocidad de ejecución y el uso de memoria.

Para el software de investigación, agregamos:

  • Validación numérica: pruebas que dan cuenta de las tolerancias de punto flotante y la variación estocástica.
  • Verificaciones de reproducibilidad: verificación de que los resultados coinciden con los resultados de referencia dentro de los límites aceptables.
  • Validación de datos: garantiza la integridad de los datos de entrada y salida.

Componentes centrales: Construyendo una tubería de CI lista para la investigación

Una tubería de CI sólida para proyectos científicos de Python debe incluir estos componentes, cada uno de los cuales aborda un aspecto de calidad específico.

Pruebas automatizadas con PyTest

La Fundación es un conjunto de pruebas completo que utiliza pytest. PyTest es el estándar de facto para las pruebas de Python debido a su simplicidad, potentes accesorios y rico ecosistema.

Para el código científico, concéntrese en:

  • Pruebas unitarias para funciones individuales (por ejemplo, ¿un solucionador de difusión calcula correctamente en una malla simple?).
  • Pruebas de regresión que comparan los resultados con los buenos resultados conocidos (esencial para los solucionadores de PDE).
  • Pruebas basadas en la propiedad usando hipótesis para generar entradas aleatorias y verificar invariantes.

La prueba unitaria para el borrador del Código Científico (en progreso) cubre en profundidad las estrategias de PyTest, incluido el manejo de la precisión numérica.

Manejo de comparaciones numéricas

El código científico se ocupa de la aritmética de punto flotante, donde la igualdad exacta suele ser imposible debido a errores de redondeo. PyTest proporciona pytest.approx para comparaciones aproximadas:

def test_diffusion_result():
    result = run_simulation()
    expected = 0.123456
    assert result == pytest.approx(expected, rel=1e-6)  # 0.1% tolerance

Para matrices, use numpy.testing.assert_allclose:

import numpy.testing as npt

def test_field_solution():
    computed = solve_pde()
    reference = load_reference_solution()
    npt.assert_allclose(computed, reference, rtol=1e-5, atol=1e-10)

Elija tolerancias basadas en la precisión de la física y la discretización. Documente por qué se eligieron tolerancias específicas.

Medición de cobertura de código

La cobertura de código mide cuánto de su base de código se ejecuta durante las pruebas. Si bien la cobertura del 100% no siempre es necesaria (o alcanzable), el seguimiento de la cobertura ayuda a identificar las rutas de código no probadas.

Utilice pytest-cov para generar informes de cobertura:

pytest --cov=src/ --cov-report=xml --cov-report=html

Integre con codecov o coveralls para rastrear la cobertura a lo largo del tiempo y hacer cumplir los umbrales mínimos en CI.

El guía de desarrollo de Python científico proporciona ejemplos detallados de configuración de cobertura.

Análisis estático y pelusa

Las herramientas de análisis estático detectan errores y aplican la coherencia del estilo antes de que se fusione el código:

  • Flake8: Aplicación de la guía de estilo de PEP 8 y verificación básica de errores.
  • MyPy: comprobación de tipo estático (la escritura gradual es valiosa incluso en el código de investigación).
  • negro: formato automático de código (elimina los debates de estilo).
  • Pylint: análisis de calidad de código más profundo (utilícelo con cautela; algunas reglas pueden ser demasiado estrictas para el código de investigación).

Ejecute estos como trabajos de CI separados para que las fallas no bloqueen las iteraciones de prueba rápida.

Consistencia del entorno con Docker o Conda

Uno de los mayores desafíos de reproducibilidad es el infierno de la dependencia: las diferentes versiones de las bibliotecas producen resultados diferentes. CI elimina esto instalando dependencias en un entorno limpio y controlado.

Opción A: Docker (Recomendado para CI)

Docker proporciona una completa contenedorización a nivel de sistema. A Dockerfile define el entorno exacto:

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

Boettiger (2015) argumenta que Docker «es lo mejor que le ha pasado a la reproducibilidad científica» porque bloquea toda la pila de software, desde el sistema operativo hasta las bibliotecas.

Opción B: Entornos de Conda

Si su proyecto se basa en dependencias que no son de Python (p. ej., HDF5, MPI), utilice Conda:

# environment.yml
name: research-ci
dependencies:
  - python=3.11
  - numpy>=1.24
  - scipy
  - pip:
    - pytest
    - pytest-cov

Los sistemas CI pueden crear y activar este entorno con conda env create -f environment.yml.

Importante: Docker no garantiza la reproducibilidad advierte que incluso los contenedores pueden tener diferencias sutiles (timestamps, aleatorio semillas). Para una máxima reproducibilidad, también corrija las versiones de la biblioteca y las semillas.

Edificio de documentación

Incluya un paso para crear la documentación (Sphinx, MKDOC) y, opcionalmente, despliegue. La documentación como código garantiza que los documentos se mantengan sincronizados con el código. El borrador de paquetes de Python Scientific Python analiza esto en detalle.

Puntos de referencia de rendimiento

Para software de investigación computacionalmente intensivo, monitoree el rendimiento para detectar regresiones. Herramientas como asv (velocidad airspeed) Ejecutar puntos de referencia automáticamente y comparar con ejecuciones anteriores.

Waller et al. (2015) describen incluidos los puntos de referencia de rendimiento en CI para detectar las degradaciones de rendimiento antes de tiempo. Esto es particularmente importante para los solucionadores de PDE donde los cambios algorítmicos pueden afectar drásticamente el tiempo de ejecución.

Comparación de plataformas: Acciones de GitHub vs Gitlab CI

Existen dos plataformas de CI dominantes: GitHub Actions y Gitlab CI. Ambos son maduros y listos para la producción. La elección a menudo depende de dónde esté alojado su código.

Acciones de GitHub

Fuerzas:

  • Integración profunda con GitHub (cheques de solicitud de extracción, mercado de acciones).
  • Sintaxis de configuración más simple para flujos de trabajo comunes.
  • comunidad más grande y más acciones de terceros.
  • gratis para repositorios públicos; Nivel gratuito generoso para repositorios privados.

Debilidades:

  • Menos potente para flujos de trabajo complejos en comparación con Gitlab.
  • Funciones integradas limitadas para el almacenamiento en caché de dependencias en las primeras versiones (ahora mejoradas).
  • Atado al ecosistema de GitHub.

adopción: el 33% de las organizaciones usan acciones de GitHub (JetBrains, 2026).

GITLAB CI

Fuerzas:

  • Más rico en funciones fuera de la caja (todo en una plataforma).
  • Potentes estrategias matriciales y canalizaciones padre-hijo.
  • Mejor soporte para monorepos.
  • Opción de autohospedaje para entornos de investigación con espacios de aire.

Debilidades:

  • Curva de aprendizaje más pronunciada.
  • comunidad más pequeña que las acciones de GitHub.
  • La interfaz puede sentirse menos pulida.

Adopción: 19% de las organizaciones (JetBrains, 2026).

Recomendación

Si su código está en GitHub, use acciones de github para la simplicidad y la integración del ecosistema. Si está en GitLab o necesita funciones avanzadas de canalización, seleccione Gitlab CI. Para entornos HPC con espacios de aire, considere Gitlab autohospedado.

Ambas plataformas pueden lograr los mismos resultados; Las diferencias son en su mayoría preferencias de flujo de trabajo. Los ejemplos a continuación utilizan acciones de GitHub debido a su popularidad, pero los equivalentes de Gitlab CI son fáciles de construir.

Configuración de CI: un flujo de trabajo completo de acciones de GitHub

Esta sección proporciona un flujo de trabajo de acciones de GitHub listo para la producción para un paquete científico de Python. Adaptalo a la estructura de tu proyecto.

requisitos previos

  1. Existen pruebas (tests/ directorio).
  2. Los requisitos están anclados (requirements.txt o environment.yml).
  3. opcional pero recomendado: Dockerfile para la reproducibilidad del entorno.
  4. El repositorio de código está en GitHub.

Flujo de trabajo básico

Crear .github/workflows/ci.yml:

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12"]

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
        cache-dependency-path: 'requirements.txt'

    - name: Install dependencies
      run: |
        pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest pytest-cov

    - name: Run tests with coverage
      run: |
        pytest --cov=src/ --cov-report=xml --cov-report=term-missing --junitxml=test-results.xml

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

    - name: Upload test results
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: test-results-${{ matrix.python-version }}
        path: test-results.xml

Características clave:

  • Estrategia de matriz: las pruebas se ejecutan en Python 3.9–3.12 en paralelo, detectando problemas de compatibilidad con anticipación.
  • Caché: actions/setup-python Almacenamiento en cachés de paquetes PIP, reduciendo drásticamente el tiempo de instalación.
  • Cobertura: salida terminal y XML para CodeCoV.
  • artefactos: los resultados de las pruebas se cargan incluso si fallan las pruebas, conservando la evidencia.

Uso de Docker en CI

Si tiene un Dockerfile, úselo para garantizar la coherencia del entorno:

    - name: Build Docker image
      run: docker build -t myproject-ci -f Dockerfile.ci .

    - name: Run tests in Docker
      run: |
        docker run --rm 
          -v ${{ github.workspace }}:/app 
          myproject-ci 
          pytest --cov=src/ --cov-report=xml

Manejo de pruebas de larga duración

Las simulaciones científicas pueden tomar horas. Los corredores de CI tienen límites de tiempo (a menudo 6 horas). Estrategias:

  1. Separe las pruebas rápidas y lentas: use marcadores PyTest.
# In test file
import pytest

@pytest.mark.slow
def test_large_simulation():
    # Takes >5 minutes
    pass

en CI:

    - name: Run quick tests
      run: pytest -m "not slow"

    - name: Run slow tests (optional, separate job)
      if: github.event_name == 'schedule'  # Only on schedule, not on every PR
      run: pytest -m slow
  1. Selección de prueba: Ejecutar solo las pruebas afectadas por el cambio de código usando pytest --last-failed o pytest -k "test_name".
  2. paralelizar: divida las pruebas en varios trabajos de CI usando pytest-xdist.

Dependencias de almacenamiento en caché

Más allá del almacenamiento en caché de paquetes de Python, extensiones compiladas de caché y archivos de datos grandes:

    - name: Cache pip packages
      uses: actions/cache@v4
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
        restore-keys: |
          ${{ runner.os }}-pip-

    - name: Cache pytest
      uses: actions/cache@v4
      with:
        path: .pytest_cache
        key: ${{ runner.os }}-pytest-${{ hashFiles('**/*.py') }}

Agregando pelusas

Agregue un trabajo separado para que los problemas de estilo no bloqueen la ejecución de la prueba:

  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: "3.11"
    - run: pip install flake8 black mypy
    - run: flake8 src/ tests/
    - run: black --check src/ tests/
    - run: mypy src/

Trampas comunes y cómo evitarlas

Basado en los desafíos de CI/CD identificados en el software de investigación (testmu AI, 2026), aquí hay errores y soluciones frecuentes.

Escoma 1: Pruebas que escama

Las pruebas escamosas pasan a veces y fallan a otras, erosionando la confianza en CI. Son especialmente comunes con:

  • Condiciones de carrera en pruebas paralelas.
  • Supuestos de tiempo (por ejemplo, «espera 1 segundo»).
  • Aleatoriedad sin semillas fijas.

Solución: Determine todo. Use pytest accesorios con scope="session" para recursos compartidos. Establezca semillas aleatorias al comienzo de cada prueba:

import random
import numpy as np

def setup_function():
    random.seed(42)
    np.random.seed(42)

Escoma 2: CI que lleva demasiado tiempo

Si su canalización toma horas, los desarrolladores lo pasarán por alto.

Solución:

  • Dividido en trabajos rápidos (en cada compromiso) y lentos (noche).
  • Caché agresivamente (PIP, capas de Docker, datos de prueba).
  • Paralelice utilizando estrategias de matriz.
  • Marque las pruebas de Slow Know con @pytest.mark.slow y ejecútelas por separado.

Escollo 3: Deriva del medio ambiente entre CI y desarrollo

Las pruebas pasan en CI pero fallan localmente porque los entornos difieren.

Solución: use la misma definición de entorno en todas partes. Docker es ideal: los desarrolladores ejecutan docker-compose run test localmente y CI usa el mismo Dockerfile. Alternativamente, use tox para administrar múltiples entornos de manera consistente.

Escollo 4: Dependencias faltantes o desactualizadas

CI falla porque se actualizó una dependencia ascendente y se rompió la compatibilidad.

Solución: Dependencias de pines exactamente en requirements.txt (package==1.2.3), no con rangos (>=1.0). Utilice un archivo de bloqueo de dependencia (pip freeze > requirements.txt). Actualice regularmente las dependencias de manera controlada (por ejemplo, PRS semanal dependabot).

Escolar 5: Sin monitoreo de rendimiento

El código se vuelve más lento con el tiempo, pero solo se nota cuando es catastrófico.

Solución: agregue puntos de referencia a CI con asv. Configúrelo para que falle si el rendimiento se degrada más allá de un umbral (por ejemplo, un 5% más lento). Consulte guía de la velocidad de Python para la implementación.

Escolar 6: Ignorar la validación numérica

Las pruebas usan == en flotadores y fallan de forma intermitente, o peor, pasan incorrectamente.

Solución: use pytest.approx y numpy.testing.assert_allclose en todas partes. Elija tolerancias basadas en el análisis numérico (por ejemplo, el error de discretización debe ser O(H²) para los métodos de segundo orden). Justificación de la tolerancia del documento en las cadenas de documentos de prueba.

Guía de Decisiones: Cuándo usar lo que

Selección de plataforma

Situación plataforma recomendada
Código alojado en GitHub Acciones de GitHub
Código alojado en Gitlab GITLAB CI
Necesita corredores auto-hospedados (con espacios en el aire) Gitlab CI (auto-hospedado)
Quiere la configuración más simple Acciones de GitHub
Pipelines complejos multiproyectos Gitlab CI (Tuberías padre-hijo)

estrategia de prueba

Tipo de código Enfoque recomendado
Funciones de pitón pura Pruebas unitarias con PyTest, objetivo de alta cobertura (>90%)
Solucionados de PDE Pruebas de regresión contra soluciones de referencia, pruebas basadas en la propiedad
Algoritmos estocásticos Semilla aleatoria fija + pruebas estadísticas (media, varianza)
Simulaciones grandes (>5 min) Separe las pruebas lentas, ejecute todas las noches; Usar @pytest.mark.slow
Acoplamiento multicomponente Pruebas de integración con pequeños casos de prueba, validar la corrección de acoplamiento

Opción de contenedor

Necesidad Recomendación
Máxima reproducibilidad, incluye DEPS de nivel OS Estibador
Gestión de Python, más simple Medio Ambiente
HPC con bibliotecas MPI conda (o acoplador con --network=host y --ipc=host)
Entorno con espacios de aire Conda Pack o Docker Guardar/Cargar

Integración de CI con flujos de trabajo de investigación

CI no existe de forma aislada. Se conecta con otras herramientas y prácticas.

Integración de seguimiento de problemas

El estado de CI aparece automáticamente en las solicitudes de extracción de GitHub/Gitlab. Configure las reglas de protección de rama para requerir el paso de CI antes de la combinación. Esto garantiza que solo el código validado entre en la rama principal.

Las publicaciones existentes de MatForge en seguimiento de problemas y deuda técnica complementar CI definiendo cómo se gestionan los problemas. CI proporciona verificación automatizada de que los problemas se solucionan correctamente.

Conexión de reproducibilidad

Como se discutió en reproducibilidad y su papel en la depuración, CI es una piedra angular de la investigación reproducible. Se puede confiar en cada confirmación que pasa CI para producir los mismos resultados en cualquier máquina con el mismo entorno. Esto es esencial para:

  • Reproducibilidad del papel: cuando los revisores piden código, puede señalar un compromiso específico que pasó CI y produjo las cifras.
  • Colaboración: los contribuyentes externos pueden ejecutar las mismas pruebas localmente.
  • Mantenimiento a largo plazo: años más tarde, aún puede reconstruir los resultados de una confirmación CI-validada.

Flujo de trabajo de revisión de código

Empareja CI con revisión de código obligatoria:

  1. El desarrollador empuja la rama, CI se ejecuta.
  2. Si pasa CI, abra una solicitud de extracción.
  3. Los revisores verifican la lógica del código y aseguran que las pruebas sean adecuadas.
  4. Fusionar solo después de que CI pase y revisar aprobado.

Este flujo de trabajo es estándar en la industria, pero aún es raro en la investigación. Implementarlo aumenta drásticamente la calidad del software.

Temas avanzados

Pruebas de matriz para múltiples dependencias

Los paquetes científicos a menudo dependen de Numpy/Scipy con un comportamiento específico de versión. Pruebe a través de una matriz de versiones de Python y dependencias:

strategy:
  matrix:
    python-version: ["3.9", "3.10", "3.11"]
    numpy-version: ["1.24", "1.25", "1.26"]

Instale la versión numpy específica en el paso Install dependencies:

    - run: |
        pip install "numpy==${{ matrix.numpy-version }}" scipy

Esto detecta problemas de compatibilidad con anticipación.

Detección de regresión de rendimiento

Use asv para realizar un seguimiento del rendimiento a lo largo del tiempo:

    - name: Run benchmarks
      run: |
        asv run --quick --show-stderr
      # asv compares against previous commits and reports regressions

Configure ASV para que falle el trabajo de CI si un punto de referencia es >10% más lento que la ejecución anterior. Consulte artículo de pythonspeed para obtener más detalles.

Despliegue continuo de documentación

CI puede implementar la documentación automáticamente en las páginas de GitHub:

  deploy-docs:
    needs: test  # Only run after tests pass
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: pip install -r requirements-docs.txt
    - run: sphinx-build -b html docs/ public/
    - uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./public

Esto mantiene la documentación sincronizada con los cambios de código.

Guías relacionadas

Resumen y próximos pasos

La integración continua transforma el software de investigación de scripts frágiles e indocumentados en activos confiables y mantenibles. Los pasos centrales son:

  1. Escriba las pruebas automatizadas con PyTest, usando pytest.approx para comparaciones numéricas.
  2. Configure una canalización de CI (Acciones de GitHub o CI de Gitlab) que se ejecuta en cada solicitud de empuje y extracción.
  3. Use Docker o Conda para garantizar la coherencia del entorno entre CI y el desarrollo.
  4. Agregue informes de cobertura, pelusas y creación de documentación.
  5. Supervise el rendimiento con puntos de referencia para capturar regresiones.
  6. Integre CI con sus procesos existentes de seguimiento de problemas y revisión de código.

Acciones inmediatas:

  • Si no tiene pruebas, comience escribiendo algunas para las funciones más críticas. Incluso la cobertura del 20% es mejor que ninguna.
  • Cree un archivo de configuración de CI básico (.github/workflows/ci.yml como se muestra arriba) e iterar.
  • Arregle las pruebas escamosas de inmediato: erosionan la confianza.
  • Agregue una «insignia» a su Léame que muestra el estado de CI (por ejemplo, IC).

Cuándo buscar consulta: si su proyecto involucra dependencias complejas (MPi, código GPU, bibliotecas patentadas) o tiene >10,000 líneas de código, considere una revisión profesional de su configuración de CI. Ofrecemos servicios de implementación personalizados de CI/CD para equipos de investigación.

Referencias y lectura adicional


Recuento de palabras: ~2,200
Tiempo de lectura: ~10 minutos
Audiencia de destino: investigadores, estudiantes graduados y desarrolladores que trabajan en proyectos científicos de Python que necesitan establecer una calidad automatizada y confiable seguridad.