"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Unidad de prueba de la API Image Uploader con JUnitnd Mockito

Unidad de prueba de la API Image Uploader con JUnitnd Mockito

Publicado el 2024-08-19
Navegar:168

Unit Testing the Image Uploader API with JUnitnd Mockito

En el primer artículo de esta serie, analizamos la creación de un cargador de imágenes sólido utilizando Spring Boot, Cloudinary, Docker y PostgreSQL. Cubrimos todo, desde la configuración del proyecto hasta la realización de solicitudes hasta el punto final que guarda la imagen y la información. Si aún no ha leído ese artículo, le recomiendo comenzar allí para obtener una base sólida de la aplicación con la que trabajaremos.

Ahora es el momento de garantizar que nuestra aplicación sea confiable y mantenga su integridad a lo largo del tiempo. Esto nos lleva a un aspecto crucial del desarrollo de software: pruebas. En este artículo, nos centraremos en escribir pruebas unitarias para nuestra API de carga de imágenes. Exploraremos cómo simular dependencias y escribiremos pruebas que cubran diferentes partes de nuestro servicio.

Las pruebas unitarias no solo ayudan a detectar errores temprano, sino que también garantizan que nuestro código sea mantenible y escalable. Al final de este artículo, tendrá un conjunto completo de pruebas para su API de carga de imágenes, lo que le brindará la confianza de que su aplicación funciona como se esperaba.

¡Sumerjámonos en el mundo de las pruebas unitarias y hagamos que nuestra API de carga de imágenes sea a prueba de balas!

Configurando

Estoy usando VSCode con el paquete de extensión para Java. Ahora estamos listos para escribir nuestras pruebas.

Si está utilizando otro IDE, consulte el soporte para todos ellos aquí en la documentación de JUnit5.

Pruebas

1. Reservar pruebas de servicio

Haga clic derecho en la clase BookService, haga clic en Ir a prueba y seleccione los métodos para los que desea generar pruebas en el menú.

Se generará un archivo similar, como el siguiente:

import org.junit.jupiter.api.Test;

public class BookServiceTest {
    @Test
    void testAddBook() {

    }
}

Recuerde, para este artículo, vamos a utilizar el patrón AAA de prueba (Organizar - Actuar - Afirmar).

1.1. Propiedades burlonas

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

    @InjectMocks
    private BookService bookService;

}
  • Las anotaciones @Mock simulan/burlan el comportamiento de las propiedades o dependencias que serán utilizadas por la clase.
  • La anotación @InjectMocks crea e inyecta los simulacros en los campos correspondientes.

1.2. Pruebas de escritura

  • Probando un caso de éxito (debería crear un nuevo libro).
  • Probando una llamada al repositorio (debería CallRepositorySave).
  • Probando si la carga falla (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 bookArgumentCaptor;

    @InjectMocks
    private BookService bookService;

    @Nested
    class AddBook {
        @Test
        void shouldCreateANewBook() throws Exception {
            // Arrange
            Map 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 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. Pruebas del controlador de libros

  • Probando un caso de éxito (shouldReturnSuccess)
  • Probando un caso fallido (shouldFailToUploadImage)
  • Prueba con un parámetro de nombre faltante (shouldFailWithMissingNameParameter)
  • Prueba con un parámetro imgUrl faltante (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());
        }
    }

}

Conclusión

Estos son algunos casos de prueba simples para que comiences a probar tu aplicación. Recuerde, podemos refactorizar estas pruebas agregando algunas fábricas para evitar repeticiones.

Gracias por leer.

Referencia

JUnit5 - Documentos
Mockito - Documentos

Declaración de liberación Este artículo se reproduce en: https://dev.to/mspilari/unit-testing-the-image-uploader-api-with-junit5-and-mockito-ge1?1 Si hay alguna infracción, comuníquese con Study_golang@163 .com para eliminarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3