在本系列的第一篇文章中,我们逐步介绍了如何使用 spring boot、cloudinary、docker 和 postgresql 构建一个强大的图像上传器。我们涵盖了从设置项目到向保存图像和信息的端点发出请求的所有内容。如果您还没有阅读那篇文章,我强烈建议您从那里开始,为我们将要使用的应用程序打下坚实的基础。

现在,是时候确保我们的应用程序可靠并随着时间的推移保持其完整性。这给我们带来了软件开发的一个关键方面:测试。在本文中,我们将重点为我们的图像上传器 api 编写单元测试。我们将探索如何模拟依赖关系,并编写涵盖服务不同部分的测试。

单元测试不仅有助于及早发现错误,还能确保我们的代码可维护和可扩展。在本文结束时,您将拥有一套针对图像上传器 api 的全面测试,让您确信您的应用程序按预期工作。

让我们深入单元测试的世界,让我们的图像上传器 api 防弹!

配置

我正在使用 vscode 和 java 扩展包。现在我们准备好编写测试了。

如果您使用其他 ide,请参阅 junit5 文档中对所有这些 ide 的支持。

测试

  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<book> bookargumentcaptor;

@injectmocks
private bookservice bookservice;

}
登录后复制

@mock 注释模拟/模拟类将要使用的属性或依赖项的行为。

@injectmocks 注解创建模拟并将其注入到相应的字段中。

1.2.编写测试

测试成功案例(shouldcreateanewbook)。
测试对存储库的调用(shouldcallrepositorysave)。
测试上传是否失败(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 &amp; assert
        responsestatusexception exception = assertthrows(responsestatusexception.class, () -&gt; {
            bookservice.addbook("test book", multipartfile);
        });

        assertequals(httpstatus.bad_gateway, exception.getstatuscode());
        assertequals("failed to upload the file.", exception.getreason());
    }
}

}
登录后复制

  1. 图书控制器测试

测试成功案例(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 &amp; 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 &amp; Assert
        mockMvc.perform(multipart("/addBook")
                .file(image)
                .param("name", "Test Book"))
                .andExpect(status().isInternalServerError())
                .andExpect(result -&gt; 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 &amp; Assert
        mockMvc.perform(multipart("/addBook")
                .file(image))
                .andExpect(status().isBadRequest());
    }

    @Test
    void shouldFailWithMissingImageParameter() throws Exception {
        // Arrange
        mockMvc = MockMvcBuilders.standaloneSetup(bookController).build();

        // Act &amp; Assert
        mockMvc.perform(multipart("/addBook")
                .param("name", "Test Book"))
                .andExpect(status().isBadRequest());
    }
}

}
登录后复制

结论

这些是一些简单的测试用例,供您开始测试您的应用程序。请记住,我们可以通过添加一些工厂来重构这些测试以避免重复。

感谢您的阅读。

参考

junit5 - 文档
mockito - 文档

    以上就是使用 JUnitnd Mockito 对图像上传器 API 进行单元测试的详细内容,更多请关注php中文网其它相关文章!