Aller au contenu principal
TestsCI/CDSonarQubeQualitéDevOps

Tests dans les pipelines CI/CD et Quality Gates

30 min de lecture Tests & Qualité — Chapitre 4

Intègre les tests dans tes pipelines CI/CD. SonarQube, coverage reports, quality gates et stratégies de test en production.

Objectif : À la fin de ce chapitre, tu sauras intégrer les tests dans un pipeline CI/CD, configurer SonarQube pour l’analyse de qualité, et mettre en place des quality gates qui bloquent le déploiement si la qualité n’est pas au rendez-vous.

Durée estimée : 35 minutes | Niveau : Intermédiaire

Introduction

Tu sais écrire des tests unitaires et d’intégration. Mais si personne ne les lance, ils servent à rien. Le vrai pouvoir des tests, c’est quand ils tournent automatiquement à chaque commit, à chaque PR, à chaque déploiement.

Dans ce chapitre, on va brancher les tests dans des pipelines CI/CD et mettre en place des quality gates — des barrières automatiques qui empêchent du code de mauvaise qualité d’atteindre la production.

Pourquoi tester dans le pipeline ?

La réponse est simple : les humains oublient. Même les meilleurs développeurs oublient de lancer les tests avant de push. Un pipeline CI/CD, lui, n’oublie jamais.

Les bénéfices concrets :

  • Détection immédiate des régressions
  • Feedback rapide — tu sais en 2 minutes si ton code casse quelque chose
  • Confiance — si le pipeline est vert, le code est déployable
  • Documentation vivante — les tests montrent ce que le code est censé faire

Tests dans GitHub Actions

Voici un pipeline complet qui lance les tests à chaque push et PR :

name: Tests & Quality

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

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: test_db
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-test.txt

      - name: Run linting
        run: |
          ruff check .
          ruff format --check .

      - name: Run tests with coverage
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test_db
        run: |
          pytest --cov=app --cov-report=xml --cov-report=html --junitxml=test-results.xml -v

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: htmlcov/

      - name: Check coverage threshold
        run: |
          coverage report --fail-under=80

Ce qu’on fait ici :

  • Service PostgreSQL pour les tests d’intégration
  • Linting avec Ruff (formatage + style)
  • Tests avec pytest + coverage
  • Seuil de couverture à 80% — le pipeline échoue si c’est en dessous

Tests dans GitLab CI

L’équivalent en GitLab :

stages:
  - lint
  - test
  - quality

lint:
  stage: lint
  image: python:3.12-slim
  script:
    - pip install ruff
    - ruff check .
    - ruff format --check .

test:
  stage: test
  image: python:3.12-slim
  services:
    - postgres:16
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
    DATABASE_URL: postgresql://test:test@postgres:5432/test_db
  script:
    - pip install -r requirements.txt -r requirements-test.txt
    - pytest --cov=app --cov-report=xml --cov-report=term --junitxml=report.xml -v
  artifacts:
    reports:
      junit: report.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
  coverage: '/TOTAL.*\s+(\d+%)/'

GitLab CI a un avantage : les rapports de couverture sont intégrés nativement dans les merge requests. Tu vois directement quel pourcentage de code est couvert.

SonarQube — Analyse de qualité

SonarQube va plus loin que la couverture. Il analyse :

  • Bugs potentiels
  • Vulnérabilités de sécurité
  • Code smells (dette technique)
  • Duplication de code
  • Couverture de tests

Installation avec Docker

# docker-compose.yml
services:
  sonarqube:
    image: sonarqube:lts-community
    ports:
      - "9000:9000"
    environment:
      SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: "true"
    volumes:
      - sonar_data:/opt/sonarqube/data
      - sonar_logs:/opt/sonarqube/logs

volumes:
  sonar_data:
  sonar_logs:

Intégration dans le pipeline

sonarqube:
  stage: quality
  image: sonarsource/sonar-scanner-cli:latest
  variables:
    SONAR_HOST_URL: "https://sonar.devopslab.ch"
    SONAR_TOKEN: $SONAR_TOKEN
  script:
    - sonar-scanner
      -Dsonar.projectKey=mon-projet
      -Dsonar.sources=app/
      -Dsonar.tests=tests/
      -Dsonar.python.coverage.reportPaths=coverage.xml
      -Dsonar.python.xunit.reportPath=report.xml
  allow_failure: false

Configuration du projet (sonar-project.properties)

sonar.projectKey=mon-projet
sonar.projectName=Mon Projet DevOps
sonar.sources=app/
sonar.tests=tests/
sonar.language=py
sonar.python.coverage.reportPaths=coverage.xml

# Quality Gate personnalisé
sonar.qualitygate.wait=true

Quality Gates

Un quality gate est un ensemble de conditions que le code doit remplir pour être considéré comme “déployable”. Si une condition échoue, le pipeline est bloqué.

Conditions typiques

Coverage         ≥ 80%     (couverture de tests)
Duplications     ≤ 3%      (code dupliqué)
Bugs             = 0       (aucun bug détecté)
Vulnerabilities  = 0       (aucune vulnérabilité)
Code Smells      ≤ 10      (dette technique limitée)

Quality Gate dans GitHub Actions

      - name: Quality Gate
        run: |
          # Check coverage
          COV=$(coverage report | grep TOTAL | awk '{print $NF}' | tr -d '%')
          if [ "$COV" -lt 80 ]; then
            echo "Coverage $COV% is below 80% threshold"
            exit 1
          fi

          # Check for security issues
          pip install safety
          safety check --json || true

          # Check complexity
          pip install radon
          radon cc app/ -a -nc

Coverage reports dans les PR

Le but ultime : quand quelqu’un ouvre une PR, le pipeline affiche automatiquement :

  • Le pourcentage de couverture actuel
  • La diff de couverture (est-ce que la PR améliore ou dégrade la couverture ?)
  • Les fichiers non couverts

Avec Codecov (gratuit pour l’open source)

      - name: Upload to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage.xml
          fail_ci_if_error: true

Avec SonarQube

SonarQube crée automatiquement un commentaire dans la PR avec un résumé : bugs trouvés, couverture, duplications, quality gate pass/fail.

Stratégies de test par environnement

graph LR
  Local["Local (dev)
Unit tests, linting"] --> PR["PR/MR
Unit + integration
+ coverage"] PR --> Staging["Staging
E2E + performance
+ security"] Staging --> Prod["Production
Smoke tests
+ synthetics"] style Local fill:#1e3a5f,stroke:#3b82f6,color:#f1f5f9 style PR fill:#1a2332,stroke:#3b82f6,color:#f1f5f9 style Staging fill:#1a2332,stroke:#f59e0b,color:#f1f5f9 style Prod fill:#1a2332,stroke:#dc2626,color:#f1f5f9

L’idée : plus tu te rapproches de la prod, plus les tests sont lourds mais aussi plus ils sont critiques.

Tests de performance dans le pipeline

  performance:
    stage: test
    image: grafana/k6:latest
    script:
      - k6 run --out json=results.json tests/performance/load_test.js
    artifacts:
      paths:
        - results.json

Avec k6, tu peux définir des seuils :

// tests/performance/load_test.js
export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% des requêtes < 500ms
    http_req_failed: ['rate<0.01'],    // Moins de 1% d'erreurs
  },
  stages: [
    { duration: '30s', target: 20 },
    { duration: '1m', target: 50 },
    { duration: '30s', target: 0 },
  ],
};

À toi de jouer

Trois exercices pour ancrer ce que tu viens de lire.

Exercice 1 — Pipeline GitHub Actions avec tests Crée un workflow GitHub Actions qui : installe les dépendances Python, lance les tests unitaires avec pytest, génère un rapport de coverage en XML, et poste le résumé de coverage en commentaire sur la PR (avec pytest-cov et une action de reporting).

Exercice 2 — Quality Gate avec SonarQube Déploie SonarQube avec Docker Compose. Configure un projet, définis un Quality Gate custom (coverage > 80%, aucun bug critique, dette technique < 2h). Intègre l’analyse dans ton pipeline CI et fais échouer le build si le Quality Gate n’est pas passé.

Exercice 3 — Stratégie de tests par environnement Conçois une matrice de tests pour 3 environnements : en PR (tests unitaires + lint, < 3 min), en staging (tests d’intégration + performance basique, < 15 min), en pre-prod (tests e2e + load test, < 1h). Écris les fichiers de workflow correspondants et configure les triggers appropriés (on push, on merge, scheduled).

Résumé

  • Les tests dans le pipeline garantissent que chaque commit est vérifié automatiquement
  • SonarQube analyse la qualité au-delà de la simple couverture (bugs, vulnérabilités, dette technique)
  • Les quality gates bloquent le déploiement si les critères ne sont pas remplis
  • La couverture de code doit être visible dans les PR (Codecov, SonarQube)
  • Adapte les types de tests à l’environnement (unit en local, E2E en staging, smoke en prod)

La suite

Tu as maintenant toutes les bases pour écrire des tests et les intégrer dans tes pipelines. La prochaine étape ? Applique tout ça sur un vrai projet — crée une API FastAPI, écris les tests, configure SonarQube, et mets en place un pipeline complet. C’est le meilleur moyen d’ancrer ces compétences.

🖥️ Pratique sur ton propre serveur

Pour suivre Tests & Qualité en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.

Obtenir 200$

Articles liés