Aller au contenu

Environnement de tests

Dans cette section, nous allons voir comment j’ai conçu un environnement de tests avec Docker. Pour cela, j’ai créée deux Dockerfile (un pour la partie api, un autre pour la partie client)et un docker-compose.yml pour gérer l’orchestration des conteneurs.


Dockerfile

Back-end

Le Dockerfile pour la partie back-end est le suivant :

FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY gradle/ gradle/
COPY settings.gradle.kts build.gradle.kts gradlew ./
RUN chmod +x gradlew
RUN ./gradlew dependencies --no-daemon || true
COPY src/ src/
ENTRYPOINT ["./gradlew", "test", "--no-daemon"]

Expliquons un peu ce qu’il se passe ici :

  1. Image de base :

On utilise l’image eclipse-temurin:21-jdk-alpine, qui contient un JDK (Java Development Kit), nécessaire pour compiler et exécuter notre application Java. L’option alpine est une version allégée de l’image JDK, ce qui réduit la taille finale de l’image, et la version 21 est exploitée ici puisqu’il s’agit de la dernière version stable de Java au moment de l’écriture de ce document.

  1. Répertoire de travail :

La commande WORKDIR définit le répertoire de travail dans le conteneur. Toutes les commandes suivantes y seront exécutées. Ici, nous définissons /app, qui est le répertoire racine de notre application.

  1. Copie des fichiers de configuration Gradle :
    • gradle/ : contient les fichiers de configuration de Gradle.
    • settings.gradle.kts, build.gradle.kts, gradlew : fichiers de configuration et script Gradle pour lancer les tâches.

Ces deux étapes auraient pu aussi bien s’écrire en plusieurs autres, comme ceci :

COPY gradle/ gradle/
COPY settings.gradle.kts .
COPY build.gradle.kts .
COPY gradlew .

Le rendu est plus clair, mais la première méthode est plus optimisée puisqu’à chaque instruction COPY, Docker crée une nouvelle couche dans l’image. Et plus il y a de couches, plus l’image est lourde.

  1. Changement des permissions :

La commande RUN chmod +x gradlew permet de rendre le script Gradle exécutable. Dans un environnement Docker, les fichiers copiés depuis l’hôte vers le conteneur peuvent perdre leurs permissions d’origine, c’est pourquoi il faut parfois les redéfinir comme ici.

  1. Téléchargement des dépendances :

La commande ./gradlew dependencies --no-daemon télécharge les dépendances nécessaires à l’application. Le paramètre --no-daemon empêche l’utilisation du démon Gradle, dont on a pas besoin ici. Cette étape est importante pour éviter de télécharger à nouveau les dépendances à chaque reconstruction de l’image .

  1. Copie du code source :

Ensuite, nous copions le répertoire src/ qui contient le code source de l’application, les fichiers Java, les ressources, etc.

  1. Commande d’entrée :

Enfin, nous définissons la commande d’entrée du conteneur, qui est ./gradlew test --no-daemon pour lancer les tests de l’application. Le paramètre --no-daemon est utilisé pour éviter de lancer un démon Gradle en arrière-plan, ce qui n’est pas nécessaire ici.

Le script Gradle test exécute les tests unitaires et d’intégration de l’application. Il génère également un rapport de tests qui peut être consulté après leur exécution .

Front-end


Docker Compose

Pour orchestrer les conteneurs, j’ai créé un fichier docker-compose-test.yml à la racine du projet :

services:
database:
container_name: db_test
image: postgres:17
restart: no
environment:
POSTGRES_DB: quizine_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
TZ: Europe/Paris
tmpfs:
- /var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
retries: 5
interval: 3s
timeout: 30s
api:
container_name: api_test
build:
context: ./api
dockerfile: Dockerfile-test
restart: no
depends_on:
database:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://database:5432/quizine_test
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: postgres
SERVER_PORT: 8081
TZ: Europe/Paris
tmpfs:
- /tmp
volumes:
- ./tests/reports/api:/app/build/reports
client:
container_name: client_test
build:
context: ./client
dockerfile: Dockerfile-test
restart: no
depends_on:
- api
environment:
TZ: Europe/Paris
profiles:
- client

Ce fichier définit trois services :

  • database : conteneur PostgreSQL pour la base de données.
  • api : conteneur pour l’application back-end Spring Boot.
  • client : conteneur pour l’application front-end Angular.

Chaque service a ses propres configurations, telles que le nom du conteneur, l’image à utiliser, les dépendances, les volumes, les ports, les variables d’environnement, etc.

Détaillons un peu tout ça !


Base de données

database:
container_name: db_test
image: postgres:17
restart: no
environment:
POSTGRES_DB: quizine_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
TZ: Europe/Paris
tmpfs:
- /var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
retries: 5
interval: 3s
timeout: 30s

Le service database utilise l’image postgres:17 pour créer un conteneur PostgreSQL.

Voici les configurations principales :

  • container_name : nom du conteneur.
  • image : image à utiliser pour le conteneur.
  • restart : politique de redémarrage du conteneur.
  • environment : variables d’environnement pour la base de données.
  • tmpfs : volume temporaire pour stocker les données de la base de données.
  • healthcheck : vérification de l’état de santé du conteneur.

Le conteneur reçoit un nom très simple db_test pour le distinguer des autres conteneurs plus facilement.

L’image postgres:17 est basée sur l’image officielle PostgreSQL disponible sur le Docker Hub. On n’utilise pas la version alpine ici, car elle ne supporte pas les locales nécessaires pour la configuration de la base de données, ce qui peut poser des problèmes lors de son initialisation ou des incohérences avec des caractères spéciaux ou des langues spécifiques.

J’utilise la version 17 puisque c’est la dernière version stable de PostgreSQL au moment de l’écriture de ce document.

La politique de redémarrage est définie sur no, ce qui signifie que le conteneur ne sera pas redémarré automatiquement en cas d’échec.

Les variables d’environnement POSTGRES_DB, POSTGRES_USER et POSTGRES_PASSWORD permettent de définir respectivement le nom de la base de données, l’utilisateur et le mot de passe. La variable TZ définit le fuseau horaire pour la base de données.

Le volume temporaire tmpfs est utilisé pour stocker les données de la base de données. Il est monté sur /var/lib/postgresql/data, qui est le répertoire de stockage par défaut de PostgreSQL. Ce volume est temporaire, ce qui signifie que les données seront perdues lorsque le conteneur sera supprimé. Cela est suffisant pour un environnement de tests, mais pas pour un environnement de production . L’usage de tmpfs permet d’accélérer les opérations d’entrée/sortie (I/O) en stockant les données en mémoire vive.

Le healthcheck est une commande qui vérifie l’état de santé du conteneur. Ici, on utilise pg_isready -U postgres pour vérifier si PostgreSQL est prêt à recevoir des connexions. Le conteneur sera considéré comme sain s’il répond correctement à cette commande. Le healthcheck est configuré pour être exécuté 5 fois, toutes les 3 secondes, avec un délai de 30 secondes avant de considérer le conteneur comme mort. Cette vérification permet de s’assurer que la base de données est prête avant de lancer les tests.


API

api:
container_name: api_test
build:
context: ./api
dockerfile: Dockerfile-test
restart: no
depends_on:
database:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://database:5432/quizine_test
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: postgres
SERVER_PORT: 8081
TZ: Europe/Paris
tmpfs:
- /tmp
volumes:
- ./tests/reports/api:/app/build/reports

Le service api utilise un Dockerfile spécifique pour construire l’image du conteneur. Ce Dockerfile est situé dans le répertoire ./api et s’appelle Dockerfile-test.

Voici les configurations principales :

  • container_name : nom du conteneur.
  • build : définition du contexte et du Dockerfile pour construire l’image.
  • restart : politique de redémarrage du conteneur.
  • depends_on : dépendance du service par rapport à un autre service.
  • environment : variables d’environnement pour l’application Spring Boot.
  • tmpfs : volume temporaire pour stocker des données temporaires.
  • volumes : montage d’un volume pour stocker les rapports de tests.

Le conteneur reçoit le nom api_test pour le distinguer des autres conteneurs plus facilement.

Le Dockerfile utilisé pour construire l’image du conteneur est spécifique aux tests. Il est situé dans le répertoire ./api et s’appelle Dockerfile-test. Il contient les instructions nécessaires pour construire l’image de l’application Spring Boot et lancer les tests.

La politique de redémarrage est définie sur no, ce qui signifie que le conteneur ne sera pas redémarré automatiquement en cas d’échec.

Le service api dépend du service database pour fonctionner. La condition service_healthy signifie que le service api ne démarrera que si le service database est considéré comme sain. Cela permet de s’assurer que la base de données est prête avant de lancer les tests.

Les variables d’environnement SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME et SPRING_DATASOURCE_PASSWORD permettent de définir respectivement l’URL de la base de données, le nom d’utilisateur et le mot de passe. La variable SERVER_PORT définit le port sur lequel l’application Spring Boot écoutera les requêtes. La variable TZ définit le fuseau horaire pour l’application.

Le volume temporaire tmpfs est utilisé pour stocker des données temporaires. Il est monté sur /tmp, qui est un répertoire temporaire dans le conteneur. Les données seront perdues lorsque le conteneur sera supprimé. C’est suffisant pour un environnement de tests, mais pas pour un environnement de production.

Le volume ./tests/reports/api:/app/build/reports est monté pour stocker les rapports de tests générés par l’application Spring Boot. Les rapports seront accessibles à l’extérieur du conteneur dans le répertoire ./tests/reports/api.


Client

client:
container_name: client_test
build:
context: ./client
dockerfile: Dockerfile-test
restart: no
depends_on:
- api
environment:
TZ: Europe/Paris
profiles:
- client