AccueilA proposContact

Test d'intégration avec Spring Boot, MongoDB et Testcontainer

Par Franck Anso
Publié dans Tech
02 févr. 2022
3 min

Spring Boot propose un bon niveau de support pour tester nos requêtes MongoDB via le module Embedded MongoDB Database. Mais il peut être intéressant d’avoir exactement la même version de base de données que celle de production.

Nous allons voir comment nous pouvons utiliser Testcontainers pour les tests d’intégration avec Spring Data MongoDB.

Qu’est-ce que Testcontainers ?

testcontainers

Testcontainers est une librairie Java qui s’appuie sur les containers Docker pour apporter une solution légère aux tests d’intégration des différentes bases de données, Selenium web browsers, etc.

Testcontainers facilite les types de tests suivants :

  • Les tests d’intégration d’accès aux données. Vous pouvez utiliser une instance containerisée de MySQL, MongoDB, etc. pour tester vos requêtes.
  • Les tests d’intégration d’application. Vous pouvez faire fonctionner votre application en mode test avec toutes ses dépendances, comme les bases de données, les files de messages ou les web servers.
  • UI tests. Vous pouvez utiliser des navigateurs containerisés, compatible avec Selenium, pour conduire des tests d’UI automatisés.

Pré-requis

Vous devez avoir :

  • Docker
  • Un cadre de test JVM pris en charge :
    • JUnit 4
    • Jupiter/JUnit 5
    • Spock

Création du projet

Nous allons utiliser Spring Initializr pour créer la base de notre projet avec toutes les dépendances.

spring initializer

Sur la partie gauche, j’ai sélectionné Gradle, Java, Spring Boot 2.7.3, packaging en Jar, Java 17. Vous pouvez choisir bien entendu d’autres options si vous êtes plus à l’aise avec Java 11 ou Maven.

Sur la partie droite, nous devons prendre les dépendances Spring Data MongoDB, Testcontainers. J’ai ajouté Lombok mais ce n’est pas obligatoire.

Il n’y a plus qu’à appuyer sur le bouton GENERATE.

Une fois le projet décompressé, vous devriez trouver un fichier gradle similaire à celui-ci :

plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
}
group = 'com.aircodr'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('testcontainersVersion', "1.17.3")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:mongodb'
}
dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}

Les seules dépendances non liées à Spring Boot sont les deux de Testcontainers. La dépendance org.testcontainers:junit-jupiter contient l’extension Testcontainers pour JUnit Jupiter que nous allons utiliser pour gérer le cycle de vie de notre container.

La dépendance org.testcontainers:mongodb contient le module Testcontainers pour MongoDB.

Pour conserver les deux dépendances correctement alignées nous utilisons org.testcontainers:testcontainers-bom (Bill of Materials) dans le bloc dependencyManagement.

Un peu de code métier

Pour avoir un peu de code métier à tester, nous allons créer une classe User.

@Data
@Builder
@Document
public class User {
@Id
private String id;
private String name;
private Integer rating;
}

@Document et @Id sont des annotations Spring Data MongoDB. Elles sont semblables à @Entity et @Id en JPA.

Les annotations @Data et @Builder viennent de la librairie Lombok. Elles me permettent de ne pas avoir à générer les getters, setters, constructeurs, etc. Si vous n’avez pas ajouté Lombok vous devrez générer les méthodes manquantes.

Nous allons ensuite créer une interface UserRepository qui va étendre MongoRepository. Le fait d’étendre l’interface MongoRepository permet d’ajouter tout un tas de méthodes comme .save(), .delete(), .findAll(), etc. à notre interface UserRepository.

Nous allons en profiter pour enrichir notre interface avec la méthode findAllByRatingBetween qui permettra de récupérer une liste ordonnée de User dont la note est comprise entre deux bornes.

public interface UserRepository extends MongoRepository<User, String> {
@Query(sort = "{rating: 1}")
List<User> findAllByRatingBetween(int min, int max);
}

Même si nous l’avons ajouté, la méthode findAllByRatingBetween s’appuie beaucoup sur le framework Spring Data MongoDB et a donc peu de chance de présenter un quelconque problème.

En contexte réel, il est bien plus intéressant de concentrer ses efforts sur des requêtes plus avancées comme celles contenant des SpEL Expressions.

Mise en place du test d’intégration

Nous pouvons maintenant passer à l’écriture des tests pour l’interface CustomerRepository.

La mise en place va suivre les étapes suivantes :

  • Désactiver l’auto-configuration d’une base de données embarquée MongoDB.
  • Configurer notre base de données MongoDB via Testcontainers.
  • Démarrer la base de données avant les tests.
  • Surcharger la propriété spring.data.mongodb.uri pour la faire pointer vers la base de données car Testcontainers utilise un port aléatoire éphémère.
@Testcontainers
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class UserRepositoryTest {
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:5.0.10");
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
}
  • L’annotation @Testcontainers s’occupe du cycle de vie du container.
  • L’annotation @DataMongoTest va désactiver l’auto-configuration du projet et appliquer à la place seulement les configurations pour les tests MongoDB. Par défaut, les tests annotés avec @DataMongoTest utilise une base de données MongoDB embarquée (si disponible). Nous excluons l’auto-configuration de la base de données embarquée via la propriété exclude de l’annotation @DataMongoTest.
  • L’annotation @DynamicPropertySource va permettre de modifier l’URI de la base de données MongoDB avant le démarrage du contexte.

Une fois la configuration mise en place il ne reste plus qu’à écrire notre test.

@Testcontainers
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class UserRepositoryTest {
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:5.0.10");
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired
private UserRepository userRepository;
@AfterEach
public void cleanUp() {
userRepository.deleteAll();
}
@Test
public void shouldReturnListOfUserWithMatchingRate() {
userRepository.save(User.builder().name("John Doe").rating(1).build());
userRepository.save(User.builder().name("William Smith").rating(2).build());
userRepository.save(User.builder().name("Jane Valentine").rating(4).build());
List<User> users = userRepository.findAllByRatingBetween(0, 3);
assertEquals(2, users.size());
assertEquals("John Doe", users.get(0).getName());
assertEquals("William Smith", users.get(1).getName());
}
}

Il ne vous reste plus qu’à tester.

Pour aller plus loin, sachez qu’il est possible de configurer Testcontainers pour pouvoir réutiliser un container Docker déjà démarré ou en démarrer un nouveau pour chaque test.

Le code source de cet exemple est disponible sur Github.

Comment je peux vous aider ?

Les technologies Spring sont au cœur de mon travail depuis plus de 10 ans.

N’hésitez pas à me contacter pour vos projets Spring Boot.


Tags

#database#test
Previous Article
Mise en place d'un environnement de dev agile

Catégories

Management
Tech
UI/UX

Publications liées

Comment migrer une base de données sans downtime ?

17 août 2022
5 min

Liens rapides

Social Media