| Prochaine révision | Révision précédente |
| eadl:bloc3:dev_av:td2-b [2025/10/08 00:30] – créée jcheron | eadl:bloc3:dev_av:td2-b [2025/10/08 01:32] (Version actuelle) – [10. Exercice pratique] jcheron |
|---|
| === 2.1 Dépendances Maven === | === 2.1 Dépendances Maven === |
| |
| <code xml> | <sxh xml> |
| <dependencies> | <dependencies> |
| <!-- Spring Boot Test (inclut JUnit 5, Mockito, AssertJ) --> | <!-- Spring Boot Test (inclut JUnit 5, Mockito, AssertJ) --> |
| </dependency> | </dependency> |
| </dependencies> | </dependencies> |
| </code> | </sxh> |
| |
| === 2.2 Configuration de test (application-test.properties) === | === 2.2 Configuration de test (application-test.properties) === |
| |
| <code properties> | <sxh bash> |
| # src/test/resources/application-test.properties | # src/test/resources/application-test.properties |
| |
| logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE | logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE |
| logging.level.org.hibernate.orm.jdbc.bind=TRACE | logging.level.org.hibernate.orm.jdbc.bind=TRACE |
| </code> | </sxh> |
| |
| ==== 3. Anatomie d'un test JPA ==== | ==== 3. Anatomie d'un test JPA ==== |
| === 3.1 Test Repository basique === | === 3.1 Test Repository basique === |
| |
| <code java> | <sxh java> |
| @DataJpaTest // ← Annotation clé | @DataJpaTest // ← Annotation clé |
| @ActiveProfiles("test") | @ActiveProfiles("test") |
| } | } |
| } | } |
| </code> | </sxh> |
| |
| <WRAP round tip> | <WRAP round tip> |
| === 3.2 TestEntityManager - Les commandes clés === | === 3.2 TestEntityManager - Les commandes clés === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void demonstrateTestEntityManager() { | void demonstrateTestEntityManager() { |
| entityManager.detach(fromDb); | entityManager.detach(fromDb); |
| } | } |
| </code> | </sxh> |
| |
| ==== 4. Tests des associations ==== | ==== 4. Tests des associations ==== |
| === 4.1 OneToMany bidirectionnel === | === 4.1 OneToMany bidirectionnel === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void shouldCascadeOrderToOrderItems() { | void shouldCascadeOrderToOrderItems() { |
| .containsExactlyInAnyOrder("Product A", "Product B"); | .containsExactlyInAnyOrder("Product A", "Product B"); |
| } | } |
| </code> | </sxh> |
| |
| === 4.2 Problème N+1 - Détection === | === 4.2 Problème N+1 - Détection === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void shouldDetectNPlusOneProblem() { | void shouldDetectNPlusOneProblem() { |
| // assertSelectCount(1); // Une seule requête | // assertSelectCount(1); // Une seule requête |
| } | } |
| </code> | </sxh> |
| |
| ==== 5. Tests des requêtes JPQL ==== | ==== 5. Tests des requêtes JPQL ==== |
| === 5.1 Query basique === | === 5.1 Query basique === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void shouldFindProductsByPriceRange() { | void shouldFindProductsByPriceRange() { |
| .containsExactly("Medium"); | .containsExactly("Medium"); |
| } | } |
| </code> | </sxh> |
| |
| === 5.2 Projection DTO === | === 5.2 Projection DTO === |
| |
| <code java> | <sxh java> |
| public record ProductSummary(UUID id, String name, BigDecimal price) {} | public record ProductSummary(UUID id, String name, BigDecimal price) {} |
| |
| assertThat(summaries.get(0).name()).isEqualTo("Test"); | assertThat(summaries.get(0).name()).isEqualTo("Test"); |
| } | } |
| </code> | </sxh> |
| |
| ==== 6. Tests avec données initiales ==== | ==== 6. Tests avec données initiales ==== |
| === 6.1 Via fichier SQL === | === 6.1 Via fichier SQL === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| @Sql("/test-data/products.sql") // ← Exécute avant le test | @Sql("/test-data/products.sql") // ← Exécute avant le test |
| assertThat(products).hasSizeGreaterThan(0); | assertThat(products).hasSizeGreaterThan(0); |
| } | } |
| </code> | </sxh> |
| |
| Fichier ''src/test/resources/test-data/products.sql'' : | Fichier ''src/test/resources/test-data/products.sql'' : |
| <code sql> | <sxh sql> |
| INSERT INTO product (id, name, price, stock) VALUES | INSERT INTO product (id, name, price, stock) VALUES |
| ('123e4567-e89b-12d3-a456-426614174000', 'Product 1', 10.00, 100), | ('123e4567-e89b-12d3-a456-426614174000', 'Product 1', 10.00, 100), |
| ('123e4567-e89b-12d3-a456-426614174001', 'Product 2', 20.00, 50); | ('123e4567-e89b-12d3-a456-426614174001', 'Product 2', 20.00, 50); |
| </code> | </sxh> |
| |
| === 6.2 Via méthode @BeforeEach === | === 6.2 Via méthode @BeforeEach === |
| |
| <code java> | <sxh java> |
| @DataJpaTest | @DataJpaTest |
| class OrderRepositoryTest { | class OrderRepositoryTest { |
| } | } |
| } | } |
| </code> | </sxh> |
| |
| ==== 7. Tests des contraintes ==== | ==== 7. Tests des contraintes ==== |
| === 7.1 Validation Bean Validation === | === 7.1 Validation Bean Validation === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void shouldFailWhenEmailInvalid() { | void shouldFailWhenEmailInvalid() { |
| .hasMessageContaining("email"); | .hasMessageContaining("email"); |
| } | } |
| </code> | </sxh> |
| |
| === 7.2 Contrainte unique === | === 7.2 Contrainte unique === |
| |
| <code java> | <sxh java> |
| @Test | @Test |
| void shouldFailOnDuplicateEmail() { | void shouldFailOnDuplicateEmail() { |
| .isInstanceOf(DataIntegrityViolationException.class); | .isInstanceOf(DataIntegrityViolationException.class); |
| } | } |
| </code> | </sxh> |
| |
| ==== 8. Testcontainers (DB réelle) ==== | ==== 8. Testcontainers (DB réelle) ==== |
| |
| <code java> | <sxh java> |
| @DataJpaTest | @DataJpaTest |
| @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) | @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) |
| } | } |
| } | } |
| </code> | </sxh> |
| |
| ==== 9. Bonnes pratiques ==== | ==== 9. Bonnes pratiques ==== |
| ==== 10. Exercice pratique ==== | ==== 10. Exercice pratique ==== |
| |
| <WRAP round box> | <WRAP round todo> |
| **À implémenter :** | **À implémenter :** |
| |
| Créer les tests d'intégration pour l'entité ''Review'' : | Créer les tests d'intégration pour l'entité ''Review'' : |
| |
| - [ ] Test de création d'une review avec associations | - Test de création d'une review avec associations |
| - [ ] Test de la contrainte ''rating'' entre 1 et 5 | - Test de la contrainte ''rating'' entre 1 et 5 |
| - [ ] Test unicité (user, product) | - Test unicité (user, product) |
| - [ ] Test du chargement avec ''JOIN FETCH'' (éviter N+1) | - Test du chargement avec ''JOIN FETCH'' (éviter N+1) |
| - [ ] Test de calcul de moyenne des ratings par produit | - Test de calcul de moyenne des ratings par produit |
| - [ ] Test de la projection ''ReviewSummaryDTO'' | - Test de la projection ''ReviewSummaryDTO'' |
| </WRAP> | </WRAP> |
| |