"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > وحدة اختبار واجهة برمجة تطبيقات Image Uploader باستخدام JUnitnd Mockito

وحدة اختبار واجهة برمجة تطبيقات Image Uploader باستخدام JUnitnd Mockito

تم النشر بتاريخ 2024-08-19
تصفح:961

Unit Testing the Image Uploader API with JUnitnd Mockito

في المقالة الأولى من هذه السلسلة، قمنا ببناء أداة تحميل صور قوية باستخدام Spring Boot وCloudinary وDocker وPostgreSQL. لقد قمنا بتغطية كل شيء بدءًا من إعداد المشروع وحتى تقديم الطلبات وحتى نقطة النهاية التي تحفظ الصورة والمعلومات. إذا لم تكن قد قرأت هذه المقالة بعد، فإنني أوصي بشدة بالبدء من هناك للحصول على أساس متين للتطبيق الذي سنعمل معه.

الآن، حان الوقت للتأكد من أن تطبيقنا موثوق به ويحافظ على سلامته مع مرور الوقت. يقودنا هذا إلى جانب حاسم من تطوير البرمجيات: الاختبار. في هذه المقالة، سنركز على كتابة اختبارات الوحدة لواجهة برمجة التطبيقات الخاصة بتحميل الصور. سنستكشف كيفية الاستهزاء بالتبعيات، وكتابة الاختبارات التي تغطي أجزاء مختلفة من خدمتنا.

لا يساعد اختبار الوحدة في اكتشاف الأخطاء مبكرًا فحسب، بل يضمن أيضًا أن الكود الخاص بنا قابل للصيانة وقابل للتطوير. بحلول نهاية هذه المقالة، سيكون لديك مجموعة شاملة من الاختبارات لواجهة برمجة التطبيقات الخاصة بتحميل الصور، مما يمنحك الثقة في أن تطبيقك يعمل كما هو متوقع.

دعونا نتعمق في عالم اختبار الوحدات ونجعل واجهة برمجة تطبيقات تحميل الصور الخاصة بنا مقاومة للرصاص!

إنشاء

أنا أستخدم VSCode مع حزمة الامتداد لـ Java. نحن الآن جاهزون لكتابة اختباراتنا.

إذا كنت تستخدم IDE آخر، راجع الدعم الخاص بهم جميعًا هنا في وثائق JUnit5.

الاختبارات

1. حجز اختبارات الخدمة

انقر بزر الماوس الأيمن على فئة BookService، وانقر على Go to Test، وحدد الطرق التي تريد إنشاء اختبارات لها من القائمة.

سيتم إنشاء ملف مشابه، مثل الملف أدناه:

import org.junit.jupiter.api.Test;

public class BookServiceTest {
    @Test
    void testAddBook() {

    }
}

تذكر، في هذه المقالة، سنستخدم نمط AAA للاختبار (ترتيب - تصرف - تأكيد).

1.1. خصائص ساخرة

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

}
  • التعليقات التوضيحية @Mock تسخر/تحاكي سلوك الخصائص أو التبعيات التي سيتم استخدامها من قبل الفصل.
  • يقوم التعليق التوضيحي @InjectMocks بإنشاء النماذج وإدخالها في الحقول المقابلة.

1.2. اختبارات الكتابة

  • اختبار حالة النجاح (يجب إنشاء كتاب جديد).
  • اختبار الاتصال بالمستودع (يجب أن يكون CallRepositorySave).
  • اختبار فشل التحميل (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. كتاب اختبارات المراقب المالي

  • اختبار حالة النجاح (shouldReturnSuccess)
  • اختبار حالة الفشل (shouldFailToUploadImage)
  • الاختبار باستخدام معلمة اسم مفقودة (shouldFailWithMissingNameParameter)
  • اختبار باستخدام معلمة imgUrl مفقودة (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());
        }
    }

}

خاتمة

هذه بعض حالات الاختبار البسيطة التي يمكنك من خلالها البدء في اختبار تطبيقك. تذكر أنه يمكننا إعادة صياغة هذه الاختبارات عن طريق إضافة بعض المصانع لتجنب التكرار.

شكرا على القراءة.

مرجع

JUnit5 - المستندات
موكيتو - المستندات

بيان الافراج تم إعادة إنتاج هذه المقالة على: https://dev.to/mspilari/unit-testing-the-image-uploader-api-with-junit5-and-mockito-ge1?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ Study_golang@163 .com لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3