Dans le premier article de cette série, nous avons expliqué la création d'un outil de téléchargement d'images robuste à l'aide de Spring Boot, Cloudinary, Docker et PostgreSQL. Nous avons tout couvert, depuis la configuration du projet jusqu'à l'envoi de requêtes au point final qui enregistre l'image et les informations. Si vous n'avez pas encore lu cet article, je vous recommande fortement de commencer par là pour avoir une base solide sur l'application avec laquelle nous allons travailler.
Maintenant, il est temps de garantir que notre application est fiable et maintient son intégrité au fil du temps. Cela nous amène à un aspect crucial du développement logiciel : les tests. Dans cet article, nous nous concentrerons sur l'écriture de tests unitaires pour notre API de téléchargement d'images. Nous explorerons comment simuler les dépendances et rédigerons des tests qui couvrent différentes parties de notre service.
Les tests unitaires aident non seulement à détecter les bogues à un stade précoce, mais garantissent également que notre code est maintenable et évolutif. À la fin de cet article, vous disposerez d'une suite complète de tests pour votre API de téléchargement d'images, vous donnant l'assurance que votre application fonctionne comme prévu.
Plongeons dans le monde des tests unitaires et rendons notre API de téléchargement d'images à l'épreuve des balles !
J'utilise VSCode avec le pack d'extension pour Java. Nous sommes maintenant prêts à écrire nos tests.
Si vous utilisez un autre IDE, consultez leur prise en charge ici dans la documentation JUnit5.
Cliquez avec le bouton droit sur la classe BookService, cliquez sur Aller au test et sélectionnez les méthodes pour lesquelles vous souhaitez générer des tests dans le menu.
Un fichier similaire, comme celui ci-dessous, sera généré :
import org.junit.jupiter.api.Test; public class BookServiceTest { @Test void testAddBook() { } }
Rappelez-vous, pour cet article, nous allons utiliser le modèle AAA de test (Arrange - Act - Assert).
@ExtendWith(MockitoExtension.class) public class BookServiceTest { @Mock private BookRepository bookRepository; @Mock private Cloudinary cloudinary; @Mock private MultipartFile multipartFile; @Mock private Uploader uploader; @Captor private ArgumentCaptor<Book> bookArgumentCaptor; @InjectMocks private BookService bookService; }
@ExtendWith(MockitoExtension.class) public class BookServiceTest { @Mock private BookRepository bookRepository; @Mock private Cloudinary cloudinary; @Mock private MultipartFile multipartFile; @Mock private Uploader uploader; @Captor private ArgumentCaptor<Book> bookArgumentCaptor; @InjectMocks private BookService bookService; @Nested class AddBook { @Test void shouldCreateANewBook() throws Exception { // Arrange Map<String, Object> uploadResult = Map.of("url", "http://example.com/image.jpg"); when(cloudinary.uploader()).thenReturn(uploader); when(uploader.upload(any(File.class), anyMap())).thenReturn(uploadResult); Book book = new Book(); book.setName("Test Book"); book.setImgUrl(uploadResult.get("url").toString()); when(bookRepository.save(any(Book.class))).thenReturn(book); when(multipartFile.getOriginalFilename()).thenReturn("test.jpg"); when(multipartFile.getBytes()).thenReturn("test content".getBytes()); // Act Book result = bookService.addBook("Test Book", multipartFile); // Assert assertNotNull(result); assertEquals("Test Book", result.getName()); assertEquals("http://example.com/image.jpg", result.getImgUrl()); } @Test void shouldCallRepositorySave() throws Exception { // Arrange Map<String, Object> uploadResult = Map.of("url", "http://example.com/image.jpg"); when(cloudinary.uploader()).thenReturn(uploader); when(uploader.upload(any(File.class), anyMap())).thenReturn(uploadResult); Book book = new Book(); book.setName("Test Book"); book.setImgUrl(uploadResult.get("url").toString()); when(bookRepository.save(any(Book.class))).thenReturn(book); when(multipartFile.getOriginalFilename()).thenReturn("test.jpg"); when(multipartFile.getBytes()).thenReturn("test content".getBytes()); // Act bookService.addBook("Test Book", multipartFile); // Assert verify(bookRepository, times(1)).save(bookArgumentCaptor.capture()); Book capturedBook = bookArgumentCaptor.getValue(); assertEquals("Test Book", capturedBook.getName()); assertEquals("http://example.com/image.jpg", capturedBook.getImgUrl()); } @Test void shouldFailTheUpload() throws Exception { // Arrange when(multipartFile.getOriginalFilename()).thenReturn("test.jpg"); when(multipartFile.getBytes()).thenReturn("test content".getBytes()); when(cloudinary.uploader()).thenReturn(uploader); when(uploader.upload(any(File.class), anyMap())).thenThrow(IOException.class); // Act & Assert ResponseStatusException exception = assertThrows(ResponseStatusException.class, () -> { bookService.addBook("Test Book", multipartFile); }); assertEquals(HttpStatus.BAD_GATEWAY, exception.getStatusCode()); assertEquals("Failed to upload the file.", exception.getReason()); } } }
package cloudinary.upload.imageUpload.controllers; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.server.ResponseStatusException; import cloudinary.upload.imageUpload.configs.GlobalExceptionHandler; import cloudinary.upload.imageUpload.entities.Book; import cloudinary.upload.imageUpload.services.BookService; @ExtendWith(MockitoExtension.class) public class BookControllerTest { @Mock private BookService bookService; @InjectMocks private BookController bookController; private MockMvc mockMvc; @Nested class AddBook { @Test void shouldReturnSuccess() throws Exception { // Arrange MockMultipartFile image = new MockMultipartFile("imgUrl", "test.jpg", MediaType.IMAGE_JPEG_VALUE, "test content".getBytes()); Book book = new Book(); book.setName("Test Book"); book.setImgUrl("http://example.com/image.jpg"); when(bookService.addBook(any(String.class), any(MockMultipartFile.class))).thenReturn(book); mockMvc = MockMvcBuilders.standaloneSetup(bookController).build(); // Act & Assert mockMvc.perform(multipart("/addBook") .file(image) .param("name", "Test Book")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Test Book")) .andExpect(jsonPath("$.imgUrl").value("http://example.com/image.jpg")); } @Test void shouldFailToUploadImage() throws Exception { // Arrange MockMultipartFile image = new MockMultipartFile("imgUrl", "test.jpg", MediaType.IMAGE_JPEG_VALUE, "test content".getBytes()); when(bookService.addBook(any(String.class), any(MockMultipartFile.class))) .thenThrow(new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to upload the file.")); mockMvc = MockMvcBuilders.standaloneSetup(bookController).setControllerAdvice(new GlobalExceptionHandler()) .build(); // Act & Assert mockMvc.perform(multipart("/addBook") .file(image) .param("name", "Test Book")) .andExpect(status().isInternalServerError()) .andExpect(result -> result.getResponse().equals("Failed to upload the file.")); } @Test void shouldFailWithMissingNameParameter() throws Exception { // Arrange MockMultipartFile image = new MockMultipartFile("imgUrl", "test.jpg", MediaType.IMAGE_JPEG_VALUE, "test content".getBytes()); mockMvc = MockMvcBuilders.standaloneSetup(bookController).build(); // Act & Assert mockMvc.perform(multipart("/addBook") .file(image)) .andExpect(status().isBadRequest()); } @Test void shouldFailWithMissingImageParameter() throws Exception { // Arrange mockMvc = MockMvcBuilders.standaloneSetup(bookController).build(); // Act & Assert mockMvc.perform(multipart("/addBook") .param("name", "Test Book")) .andExpect(status().isBadRequest()); } } }
Voici quelques cas de test simples pour commencer à tester votre application. N'oubliez pas que nous pouvons refactoriser ces tests en ajoutant quelques usines pour éviter les répétitions.
Merci d'avoir lu.
JUnit5 - Documents
Mockito - Docs
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!