Maison  >  Article  >  Java  >  Test unitaire de l'API Image Uploader avec JUnitnd Mockito

Test unitaire de l'API Image Uploader avec JUnitnd Mockito

WBOY
WBOYoriginal
2024-08-08 09:19:01734parcourir

Unit Testing the Image Uploader API with JUnitnd Mockito

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 !

Mise en place

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.

Essais

1. Réserver des tests de service

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).

1.1. Propriétés moqueuses

@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;

}
  • Les annotations @Mock simulent/simulent le comportement des propriétés ou dépendances qui vont être utilisées par la classe.
  • L'annotation @InjectMocks crée et injecte les mocks dans les champs correspondants.

1.2. Écrire des tests

  • Tester un cas de réussite (shouldCreateANewBook).
  • Test d'un appel au référentiel (shouldCallRepositorySave).
  • Test si le téléchargement échoue (shouldFailTheUpload).
@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());
        }
    }
}

2. Tests du contrôleur de livre

  • Test d'un cas de réussite (shouldReturnSuccess)
  • Test d'un cas d'échec (shouldFailToUploadImage)
  • Test avec un paramètre de nom manquant (shouldFailWithMissingNameParameter)
  • Test avec un paramètre imgUrl manquant (shouldFailWithMissingImageParameter)
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());
        }
    }

}

Conclusion

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.

Référence

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn