在本系列的第一篇文章中,我们逐步介绍了如何使用 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; } </book>
- @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 & 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()); } } } </string></string></book>
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 - 文档
mockito - 文档
以上就是使用 JUnitnd Mockito 对图像上传器 API 进行单元测试的详细内容,更多请关注php中文网其它相关文章!