Cet article a pour objectif d'ajouter et de configurer le plugin "Failsafe", d'illustrer son utilisation à travers des extraits de tests d'intégration (IT), accompagnés de recommandations selon le contexte, ainsi que d'intégrer ces étapes dans un pipeline CI GitHub Actions.
Tests Unitaires (Base): Ils forment la fondation car ils sont rapides et peu coûteux. Ils valident la logique isolée de chaque composant.
Tests d'Intégration (Milieu): Ils vérifient comment les composants interagissent et communiquent entre eux.
Ils sont plus lents que les tests unitaires mais indispensables pour détecter les erreurs d'interface.
Tests E2E / UI (Sommet): Ils testent le système complet du point de vue de l'utilisateur.
Bien qu'essentiels, ils sont maintenus en faible nombre car ils sont fragiles, lents et coûteux à exécuter.
Contrairement aux tests unitaires qui isolent le code en simulant (mock) les dépendances,
les tests d'intégration vérifient la communication et la compatibilité entre les différentes parties du code et les systèmes externes, comme une base de données réelle.
Nous utilisons ici une vraie base de données: PostgreSQL en CI, H2 ou Postgres en local selon la configuration définie
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
Les tests d'intégration Spring Boot se construisent en chargeant tout ou partie du contexte de l'application, généralement via @SpringBootTest,
pour vérifier l'interaction entre les composants (Contrôleurs, Services, Repositories)
Les tests d'intégration sont exécutés par le plugin "Failsafe",
distinct de "Surefire" qui gère les tests unitaires
Convention de nommage: Failsafe exécute par défaut les classes dont le nom se termine par IT, ITCase, etc., d'où le nom FlashcardIntegrationIT
package com.example.flashcards.integration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest
@ActiveProfiles("it")
@AutoConfigureMockMvc
class FlashcardIntegrationIT {
@Autowired private MockMvc mockMvc;
@Autowired private ObjectMapper objectMapper;
@Test
void testCRUDFLashcard() throws Exception {
CategoryDto category = new CategoryDto(null, "Math");
String categoryJson = objectMapper.writeValueAsString(category);
String categoryResponse =
mockMvc
.perform(
post("/api/categories")
.contentType(MediaType.APPLICATION_JSON)
.content(categoryJson))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
CategoryDto createdCategory = objectMapper.readValue(categoryResponse, CategoryDto.class);
}
}
→ Le script crée une catégorie pour les Flashcards
Transactions non rollback → sale état entre tests
Port random vs fixed en IT (@LocalServerPort)
# IT Behavior
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=false
# Migrations
spring.liquibase.enabled=false
spring.flyway.enabled=false
spring.sql.init.mode=never
Est en quelque sorte un fichier de profil, utilisé pour configurer la base de donnés, les ports etc. Et ce, au même titre que les fichiers situé dans src/main/resources/
Mais aussi et surtout utilisés par les classes de tests.
Ce fichier utilise un minimum de configuration, il sera configuré dans les workflows, tantôt pour H2 (local / dev), tantôt avec une réelle base de données Postgres (staging / main)
Nous devons nous referer au profil "it" qui ne dispose pas de configuration spécifiques, nous devons donc lui passer les paramètres à utiliser pour la base de données
name: CI - Develop
jobs:
build-and-test:
runs-on: ubuntu-24.04
env:
SPRING_DATASOURCE_URL: jdbc:h2:mem:itdb;DB_CLOSE_DELAY=-1
SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.h2.Driver
SPRING_DATASOURCE_USERNAME: sa
SPRING_DATASOURCE_PASSWORD:
steps:
- name: Run Maven clean verify
run: ./mvnw -B clean verify jacoco:report
Utilise H2 comme en local:
Les variables d'environements de la base de données utilise la même configuration que le profil "test", situé dans src/test/resources/application-test.properties
Le profil "dev" étant le profil par défaut, les tests étant annotés, il n'est pas nécessaire ici de préciser de profil actif
La commande verify utilisée dans le run, lance tous les tests en une fois (Spotbug, Checkstyle, Junit et tests d'intégration)
- name: Run Integration Tests
env:
SPRING_PROFILES_ACTIVE: it
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/flashcardsdb
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
run: ./mvnw -B verify -DskipUnitTests=true
Utilise une base Postgres réelle
Utilise explicitement le profil "it"
Les tests sont lancés dans une étape séparée
- name: Run Integration Tests (release only)
if: env.IS_RELEASE == 'true'
run: ./mvnw -B verify -DskipUnitTests
Comme en staging, les tests sont lancés dans une étape séparée et utilise une base réelle
Execution uniquement sur un push (pas sur les pr)
Bien qu'il soit recommandé d'utiliser testContainer en 2026, j'ai fait le choix de ne pas l'utiliser, et ce, pour plusieurs raisons:
Les développeurs peuvent lancer les tests d'intégration sans avoir Docker installé sur leur machine.
En local et sur la branche develop, les tests d'intégration utilisent H2 en mémoire, ce qui est rapide et simplifie énormément l'onboarding
Une vraie base PostgreSQL est lancée uniquement dans la CI,
via un service container GitHub Actions (staging/main)
Le pipeline se charge de démarrer et d'arrêter le container, ce qui garantit un environnement propre à chaque run