Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Apprendre Mockito | Tester les Applications Backend
Backend Spring Boot
course content

Contenu du cours

Backend Spring Boot

Backend Spring Boot

1. Notions de Base du Développement Backend
2. Notions de Base de Spring Boot
3. API RESTful
4. Travailler avec des Bases de Données
5. Tester les Applications Backend

book
Mockito

Dans le chapitre précédent, nous avons rappelé comment écrire des tests unitaires. Cependant, dans notre application, cela n'est pas toujours pratique car tester un contrôleur nécessite de tester le service, et tester le service nécessite de tester le référentiel.

Cette chaîne de dépendances soulève la question : devons-nous tester tous les modules et leurs dépendances, ou pouvons-nous simuler leur comportement — en d'autres termes, les simuler ?

Mockito : Introduction

Mockito est une bibliothèque populaire pour Java qui est utilisée pour créer des objets simulés dans les tests unitaires.

Il permet aux développeurs de simuler le comportement de dépendances externes complexes (telles que des bases de données, des services ou des API) afin d'isoler et de tester la logique d'un composant spécifique (généralement une classe ou une méthode) sans interagir avec ces systèmes externes.

Cela rend les tests unitaires plus prévisibles et rapides puisqu'il n'y a pas besoin d'appels réels à des ressources externes pendant les tests.

Objets Mock dans Mockito

Cela permet aux développeurs de s'assurer que le composant testé (l'unité) se comporte correctement sans dépendre de dépendances réelles telles que des bases de données ou des services tiers.

Une classe peut être annotée avec @Mock pour créer sa version simulée. Cette annotation est souvent utilisée avec @InjectMocks pour injecter automatiquement les dépendances dans la classe testée.

Cet objet simulé peut ensuite être injecté comme une dépendance dans un autre objet en utilisant l'annotation @InjectMocks. Cette annotation injecte automatiquement les objets simulés dans la classe testée.

Méthodes Clés dans Mockito

Mockito offre une large gamme de méthodes pour gérer les objets simulés, chacune servant un but spécifique dans le processus de test. Ci-dessous sont présentées les méthodes les plus importantes ainsi que leurs explications.

Tester les contrôleurs avec Mockito

Tester les contrôleurs implique souvent d'utiliser des mocks pour les services appelés au sein du contrôleur. Dans ce contexte, Mockito joue un rôle clé en aidant à isoler la logique du contrôleur de la couche de service.

Résumé rapide de la vidéo

Nous testons les contrôleurs en utilisant la classe MockMvc, mais que propose-t-elle et quels sont les avantages de l'utiliser ?

Avec MockMvc, vous pouvez simuler diverses requêtes HTTP (GET, POST, PUT, etc.), passer des paramètres, des en-têtes, et vérifier les réponses, les codes d'état, les en-têtes et les corps de réponse, rendant les tests unitaires des contrôleurs beaucoup plus faciles.

Par exemple, vous pouvez utiliser des méthodes comme perform() pour exécuter la requête, andExpect() pour vérifier les résultats attendus, et content() pour vérifier le contenu de la réponse.

java

Test

copy
1234
mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isOk())

De plus, vous pouvez enchaîner des assertions pour vérifier le statut HTTP en utilisant status().isOk(), vérifier les structures de réponse JSON avec jsonPath(), et plus encore.

java

Test

copy
1234
.andExpect(jsonPath("$.id").value(bookResponseDTO.getId())) .andExpect(jsonPath("$.name").value(bookResponseDTO.getName())) .andExpect(jsonPath("$.author").value(bookResponseDTO.getAuthor())) .andExpect(jsonPath("$.price").value(bookResponseDTO.getPrice()));

Ces outils permettent un test complet du comportement du contrôleur sans réellement effectuer de véritables appels HTTP.

Annotations Clés

L'annotation @WebMvcTest(BookController.class) est utilisée pour tester le BookController, en chargeant uniquement les composants nécessaires pour ce contrôleur spécifique. Elle exclut le reste de l'infrastructure Spring, vous permettant de vous concentrer sur le test du contrôleur lui-même.

De plus, l'annotation @MockBean appliquée au champ bookService crée une version simulée du service, vous permettant de simuler son comportement. Cela aide à isoler le contrôleur de ses dépendances et à se concentrer sur le test de sa logique.

Tests pour la méthode updateBook

Nous avons écrit deux tests pour la méthode updateBook qui couvrent tous les cas possibles pour cette méthode. Dans le premier test, nous vérifions que tout s'est bien passé et que notre entité a été mise à jour.

java

BookControllerTest

copy
1234567891011121314151617
@Test void testUpdateBook_whenBookExists_shouldReturnUpdatedBook() throws Exception { String bookId = "1"; when(bookService.updateBook(bookId, bookRequestDTO)).thenReturn(bookResponseDTO); mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(bookResponseDTO.getId())) .andExpect(jsonPath("$.name").value(bookResponseDTO.getName())) .andExpect(jsonPath("$.author").value(bookResponseDTO.getAuthor())) .andExpect(jsonPath("$.price").value(bookResponseDTO.getPrice())); verify(bookService).updateBook(bookId, bookRequestDTO); }

Ce test vérifie la mise à jour réussie d'un book s'il existe dans la base de données. La méthode updateBook() du service est appelée avec l'Id du livre et un objet bookRequestDTO, après quoi elle retourne un objet bookResponseDTO représentant le livre mis à jour.

Vérification de la gestion des exceptions

Nous avons également un test où une exception se produit dans la méthode updateBook, et nous devons vérifier comment le contrôleur se comporte dans cette situation.

java

BookControllerTest

copy
12345678910111213141516
@Test void testUpdateBook_whenBookNotFound_shouldReturnApiException() throws Exception { String bookId = "1"; String errorMessage = "ID not found"; when(bookService.updateBook(bookId, bookRequestDTO)) .thenThrow(new ApiException(errorMessage, HttpStatus.NOT_FOUND)); mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.error").value(errorMessage)); verify(bookService).updateBook(bookId, bookRequestDTO); }

Ce test vérifie le comportement du contrôleur lors de la tentative de mise à jour d'un livre qui n'est pas trouvé dans la base de données. La méthode updateBook() du service est d'abord appelée avec l'Id du livre et un objet bookRequestDTO.

Cependant, au lieu d'un résultat réussi, le service lance une ApiException, indiquant que le livre avec l'Id donné n'a pas été trouvé.

Test de Service avec Mockito

Lors du test d'un service, il est important de l'isoler des dépendances telles que les référentiels ou les API externes. Mockito vous permet de créer des mocks pour ces dépendances et de définir leur comportement dans les tests.

Résumé bref de la vidéo

Commençons par tester notre méthode lorsqu'elle met à jour avec succès les données, ce qui signifie que l'Id est valide. Évidemment, nous utiliserons Mockito pour simuler le comportement du référentiel.

java

BookServiceTest

copy
123456789101112131415161718192021222324252627282930313233
@Test void testUpdateBook_whenBookExists_shouldUpdateAndReturnBook() { BookRequestDTO bookRequestDTO = new BookRequestDTO(); bookRequestDTO.setName("Updated Name"); bookRequestDTO.setAuthor("Updated Author"); bookRequestDTO.setPrice("150"); Book bookWithRepository = new Book(); bookWithRepository.setId("1"); bookWithRepository.setName("Original Name"); bookWithRepository.setAuthor("Original Author"); bookWithRepository.setPrice("100"); Book updateBook = new Book(); updateBook.setId("1"); updateBook.setName("Updated Name"); updateBook.setAuthor("Updated Author"); updateBook.setPrice("150"); when(bookRepository.findById("1")).thenReturn(Optional.of(bookWithRepository)); when(bookRepository.save(bookWithRepository)).thenReturn(updateBook); BookResponseDTO result = bookService.updateBook("1", bookRequestDTO); assertNotNull(result); assertEquals("1", result.getId()); assertEquals("Updated Name", result.getName()); assertEquals("Updated Author", result.getAuthor()); assertEquals("150", result.getPrice()); verify(bookRepository).findById("1"); verify(bookRepository).save(bookWithRepository); }

La méthode bookRepository.findById("1") est simulée pour retourner un Book existant du répertoire, et bookRepository.save(bookWithRepository) est simulée pour retourner le livre mis à jour après avoir enregistré les modifications.

La méthode de service updateBook() est appelée pour mettre à jour le livre en fonction du bookRequestDTO, retournant un BookResponseDTO.

assertNotNull(result) garantit que le résultat n'est pas null, indiquant une mise à jour réussie, tandis que assertEquals() vérifie que ID, le name, author et price du livre mis à jour correspondent aux valeurs attendues.

Enfin, verify() assure que findById() et save() dans le référentiel ont été appelés avec les paramètres corrects.

Vérification d'une Exception dans le Service

Ce test vérifie que le service lance une ApiException si le Book à mettre à jour n'est pas trouvé dans la base de données.

java

BookServiceTest

copy
1234567891011121314151617181920
@Test void testUpdateBook_whenBookNotFound_shouldThrowApiException() { BookRequestDTO bookRequestDTO = new BookRequestDTO(); bookRequestDTO.setName("Updated Name"); bookRequestDTO.setAuthor("Updated Author"); bookRequestDTO.setPrice("150"); String idTest = "999"; when(bookRepository.findById(idTest)).thenReturn(Optional.empty()); ApiException apiException = assertThrows(ApiException.class, () -> { bookService.updateBook(idTest, bookRequestDTO); }); assertEquals("Not found book by id: " + idTest, apiException.getMessage()); assertEquals(HttpStatus.NOT_FOUND, apiException.getHttpStatus()); verify(bookRepository, never()).save(any(Book.class)); }

Initialement, un objet BookRequestDTO est créé avec les données pour la mise à jour, et un Id de livre de test qui n'existe pas dans la base de données "999" est attribué. La méthode bookRepository.findById(idTest) est simulée pour retourner Optional.empty(), indiquant qu'il n'y a pas de livre avec cet Id.

Le test utilise la méthode assertThrows() pour vérifier que lors de l'appel de bookService.updateBook(idTest, bookRequestDTO), une ApiException est lancée.

Ensuite, les méthodes assertEquals() vérifient que le message d'exception correspond au texte attendu et que le statut d'erreur est égal à HttpStatus.NOT_FOUND.

La méthode verify(bookRepository, never()).save(any(Book.class)) assure que la méthode de sauvegarde du livre save() n'a pas été appelée, car le livre n'a pas été trouvé et la mise à jour n'a pas été effectuée.

Résumé

Mockito est une bibliothèque pour créer des objets simulés qui vous permet de simuler le comportement des dépendances et d'isoler le code à tester. Cela simplifie les tests, les rendant plus rapides, plus stables et indépendants des ressources externes.

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 5. Chapitre 3
We're sorry to hear that something went wrong. What happened?
some-alt