eadl:bloc3:dev_av:td2-b

Test d'intégration JPA : Teste les entités, repositories et requêtes avec une vraie base de données (ou H2 en mémoire).

Différence avec test unitaire :

  • ✅ Teste les mappings JPA réels
  • ✅ Valide les contraintes DB
  • ✅ Vérifie les requêtes SQL générées
  • ✅ Détecte les problèmes N+1
  • ❌ Plus lent qu'un test unitaire

2.1 Dépendances Maven

<dependencies>
    <!-- Spring Boot Test (inclut JUnit 5, Mockito, AssertJ) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!-- H2 pour tests en mémoire -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!-- Testcontainers (optionnel, pour base réelle) -->
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>1.19.3</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2.2 Configuration de test (application-test.properties)

# src/test/resources/application-test.properties

# H2 Database en mémoire
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA/Hibernate
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

# Désactiver cache pour tests prédictibles
spring.jpa.properties.hibernate.cache.use_second_level_cache=false

# Logging SQL détaillé
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
logging.level.org.hibernate.orm.jdbc.bind=TRACE

3.1 Test Repository basique

@DataJpaTest  // ← Annotation clé
@ActiveProfiles("test")
class ProductRepositoryTest {

    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private TestEntityManager entityManager;  // ← Utilitaire de test JPA

    @Test
    void shouldSaveAndFindProduct() {
        // Given
        Product product = new Product();
        product.setName("Test Product");
        product.setPrice(new BigDecimal("99.99"));
        
        // When
        Product saved = productRepository.save(product);
        entityManager.flush();  // Force SQL immédiat
        entityManager.clear();  // Vide le cache (simule nouvelle session)
        
        // Then
        Product found = productRepository.findById(saved.getId()).orElseThrow();
        assertThat(found.getName()).isEqualTo("Test Product");
        assertThat(found.getPrice()).isEqualByComparingTo("99.99");
    }
}

Annotations essentielles :

  • @DataJpaTest : Configure uniquement la couche JPA (pas de serveur web)
  • @AutoConfigureTestDatabase(replace = NONE) : Utilise la DB configurée (pas H2 auto)
  • @Sql : Exécute un script SQL avant le test

3.2 TestEntityManager - Les commandes clés

@Test
void demonstrateTestEntityManager() {
    Product product = new Product("Laptop", new BigDecimal("1200"));
    
    // persist() : INSERT sans flush immédiat
    entityManager.persist(product);
    
    // flush() : Force l'exécution des SQL en attente
    entityManager.flush();
    
    // clear() : Vide le contexte de persistance (cache 1er niveau)
    entityManager.clear();
    
    // find() : SELECT en DB (car cache vidé)
    Product fromDb = entityManager.find(Product.class, product.getId());
    
    // detach() : Détache une entité du contexte
    entityManager.detach(fromDb);
}

4.1 OneToMany bidirectionnel

@Test
void shouldCascadeOrderToOrderItems() {
    // Given
    User user = new User("john@test.com");
    entityManager.persist(user);
    
    Order order = new Order(user);
    OrderItem item1 = new OrderItem(order, "Product A", 2);
    OrderItem item2 = new OrderItem(order, "Product B", 1);
    
    order.addItem(item1);  // Méthode helper bidirectionnelle
    order.addItem(item2);
    
    // When
    entityManager.persist(order);  // CASCADE.PERSIST sur items
    entityManager.flush();
    entityManager.clear();
    
    // Then
    Order found = entityManager.find(Order.class, order.getId());
    assertThat(found.getItems()).hasSize(2);
    assertThat(found.getItems())
        .extracting(OrderItem::getProductName)
        .containsExactlyInAnyOrder(

  • eadl/bloc3/dev_av/td2-b.txt
  • Dernière modification : il y a 10 mois
  • de jcheron