Tester une application qui parle à PostgreSQL avec une base simulée ne prouve pas grand-chose. Les service containers démarrent de vraies dépendances à côté de votre job, le temps de son exécution. Le job container, lui, change la machine sur laquelle votre code tourne. Les deux se confondent souvent, et répondent à des besoins opposés.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Démarrer une base de données ou un cache à côté du job
- Attendre qu'un service soit réellement prêt, sans
sleep - Joindre le service, selon que le job tourne sur le runner ou en conteneur
- Distinguer service container et job container, et choisir le bon
Un service, pas une simulation
Section intitulée « Un service, pas une simulation »Un service container est un conteneur que GitHub démarre avant votre job et détruit après. Votre code s'y connecte comme il se connecterait à un service réel, parce que c'en est un.
name: Tests d'intégration
on: pull_request: branches: [main]
permissions: {}
jobs: integration: runs-on: ubuntu-24.04 permissions: contents: read services: postgres: image: postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73 env: POSTGRES_PASSWORD: motdepasse-de-test POSTGRES_DB: app_test ports: - 5432:5432 options: >- --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.13" - run: pip install --require-hashes -r requirements.txt - name: Lancer les tests d'intégration env: DATABASE_URL: postgresql://postgres:motdepasse-de-test@localhost:5432/app_test run: pytest tests/integration -qL'image porte un digest, comme partout ailleurs sur ce site : un service de test épinglé sur un tag mutable rend vos tests non reproductibles, et introduit la même dépendance non maîtrisée que dans une image de production.
Attendre que le service soit prêt
Section intitulée « Attendre que le service soit prêt »C'est le piège numéro un. Un conteneur démarré n'est pas un service prêt : PostgreSQL accepte les connexions plusieurs secondes après le lancement du conteneur. Sans précaution, le premier test échoue une fois sur trois, de façon apparemment aléatoire.
La réponse n'est pas un sleep, qui est soit trop court soit trop long, mais le
health check déclaré dans options. GitHub attend que le conteneur soit
sain avant de lancer la première étape.
| Service | Commande de santé |
|---|---|
| PostgreSQL | pg_isready -U postgres |
| MySQL | mysqladmin ping |
| Redis | redis-cli ping |
| MongoDB | mongosh --eval "db.adminCommand('ping')" |
Les quatre paramètres qui les accompagnent se lisent ensemble :
--health-interval donne la fréquence des tentatives,
--health-timeout le délai avant qu'une tentative soit déclarée perdue,
--health-retries le nombre d'échecs tolérés, et --health-start-period une
période de grâce initiale utile aux services lents à démarrer.
Joindre le service : le point qui piège
Section intitulée « Joindre le service : le point qui piège »L'adresse à utiliser dépend de l'endroit où tourne votre job, et c'est la source de confusion la plus fréquente sur ce sujet.
| Le job tourne | Adresse du service | Port |
|---|---|---|
| Directement sur le runner | localhost | Le port publié via ports: |
| Dans un job container | Le nom du service | Le port interne du conteneur |
Quand le job tourne sur le runner, les services sont joignables sur localhost
grâce à la publication de ports. Quand le job tourne lui-même dans un conteneur,
il partage un réseau Docker avec les services : on les joint par leur nom,
et la publication de ports devient inutile.
integration-en-conteneur: runs-on: ubuntu-24.04 permissions: contents: read container: image: python:3.13-slim@sha256:9534e5a8e315485d4061ed659af0fd78a284c015f9b73661b41d6bab25604534 services: redis: image: redis:8-alpine@sha256:becdda6c7f4b3fb42e42fd7f120bbf5c54c4caaaf16f26da24e4563d2c1f0576 options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Lancer les tests env: REDIS_URL: redis://redis:6379 run: pytest tests/integration -qNotez l'absence de ports: sur le service, et l'adresse redis://redis:6379
qui reprend le nom de la clé déclarée sous services:.
Le job container, un autre besoin
Section intitulée « Le job container, un autre besoin »Le container: d'un job ne démarre pas une dépendance : il change la machine
d'exécution de vos étapes. Au lieu de tourner sur l'image du runner
GitHub, vos commandes tournent dans l'image que vous choisissez.
| Service container | Job container | |
|---|---|---|
| Rôle | Une dépendance à côté du job | L'environnement du job lui-même |
| Clé | services: | container: |
| Cas d'usage | Base de données, cache, file de messages | Toolchain figée, distribution précise |
| Nombre | Plusieurs | Un seul |
Le job container répond à un besoin de reproductibilité : la même image en
CI et en local, donc les mêmes versions d'outils, donc la disparition du « ça
marche sur ma machine ». Il a une contrepartie à connaître : certaines actions
supposent des outils présents sur l'image du runner et échouent dans une image
minimale. Une image slim ou alpine sans git, curl ou node peut faire
échouer actions/checkout ou d'autres actions JavaScript.
Quand ne pas utiliser un service container
Section intitulée « Quand ne pas utiliser un service container »Trois situations où le réflexe coûte plus qu'il ne rapporte.
Les tests unitaires. Si le test n'a pas besoin de la base, démarrer PostgreSQL ajoute vingt secondes à chaque exécution pour rien. Réservez les services aux tests qui traversent réellement la dépendance.
Les dépendances externes non conteneurisables. Un service tiers accessible uniquement par API ne se simule pas avec un conteneur officiel : ce sont des doublures applicatives qu'il faut, pas un service container.
Les matrices larges. Un service démarre par combinaison de la matrice. Neuf combinaisons, c'est neuf PostgreSQL. Quand le temps de démarrage domine, regrouper les tests d'intégration dans un job dédié hors matrice coûte moins cher.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un service container démarre avant le job et meurt après : le code parle à une vraie dépendance, pas à une simulation.
- L'image d'un service s'épingle par digest, comme toute autre image : un tag mutable rend les tests non reproductibles.
- Le mot de passe d'un service de test n'est pas un secret : le conteneur est éphémère et joignable du seul job.
- Un conteneur démarré n'est pas un service prêt : utilisez un
--health-cmd, jamais unsleep. - L'adresse dépend du contexte :
localhostet le port publié depuis le runner, le nom du service et le port interne depuis un job container. - Le job container change l'environnement d'exécution, pas les dépendances ; il n'y en a qu'un, et il peut manquer des outils attendus par certaines actions.
- Un job container n'est pas une frontière de sécurité : il s'exécute sur le runner et en partage le sort.
- Les services démarrent par combinaison de matrice : isolez les tests d'intégration quand le démarrage domine le temps d'exécution.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Actions composites : Factoriser la préparation répétitive d'un environnement de test entre plusieurs workflows.
- Sécuriser GitHub Actions : Ce que l'ajout d'images tierces dans vos workflows change pour la chaîne d'approvisionnement.
- Épingler par SHA : La même exigence d'immuabilité, appliquée aux actions comme ici aux images de service.