Spring Boot集成GCS Storage接口的JUnit测试编写及90%覆盖率实现求助
Spring Boot 云存储接口JUnit测试实现方案
前置代码优化(必做,否则无法实现单元测试解耦)
原有Controller中每次请求都新建Storage实例,耦合度高无法Mock,先将Storage实例交由Spring容器管理:
// GCS配置类 @Configuration public class GcsConfig { @Bean public Storage storage() throws IOException { return StorageOptions.newBuilder() .setCredentials(ServiceAccountCredentials.getApplicationDefault()) .setProjectId("projectId") .build().getService(); } }
改造后的Controller使用构造器注入依赖:
@RestController public class FileController { private final Storage storage; private final MainServiceDetails mainService; public FileController(Storage storage, MainServiceDetails mainService) { this.storage = storage; this.mainService = mainService; } @GetMapping("/getFiles") public List<String> getBucketList() { return mainService.getFileList(storage); } }
注:如果无需修改原有代码结构,也可使用Mockito的PowerMock模块Mock静态方法实现测试,耦合度较高不推荐
Service层单元测试
覆盖getFileList的所有逻辑分支,包含多文件、空文件两种场景:
import com.google.cloud.storage.Blob; import com.google.cloud.storage.Page; import com.google.cloud.storage.Storage; 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 java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class MainServiceDetailsTest { @Mock private Storage storage; @Mock private Page<Blob> blobPage; @InjectMocks private MainServiceDetails mainService; @Test void getFileList_多文件场景_返回正确文件名列表() { // 模拟云存储返回2个文件 Blob blob1 = mock(Blob.class); when(blob1.getName()).thenReturn("test1.txt"); Blob blob2 = mock(Blob.class); when(blob2.getName()).thenReturn("img/test2.jpg"); when(storage.list("bucketname")).thenReturn(blobPage); when(blobPage.iterateAll()).thenReturn(List.of(blob1, blob2)); List<String> result = mainService.getFileList(storage); assertEquals(2, result.size()); assertTrue(result.containsAll(List.of("test1.txt", "img/test2.jpg"))); } @Test void getFileList_空存储场景_返回空列表() { when(storage.list("bucketname")).thenReturn(blobPage); when(blobPage.iterateAll()).thenReturn(List.of()); List<String> result = mainService.getFileList(storage); assertTrue(result.isEmpty()); } }
Controller层单元测试
使用MockMvc测试REST接口的返回逻辑:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import com.google.cloud.storage.Storage; import org.springframework.test.web.servlet.MockMvc; import java.util.List; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(FileController.class) public class FileControllerTest { @Autowired private MockMvc mockMvc; @MockBean private Storage storage; @MockBean private MainServiceDetails mainService; @Test void getBucketList_正常请求_返回正确文件列表() throws Exception { List<String> mockList = List.of("a.txt", "b.pdf"); when(mainService.getFileList(storage)).thenReturn(mockList); mockMvc.perform(get("/getFiles")) .andExpect(status().isOk()) .andExpect(jsonPath("$").isArray()) .andExpect(jsonPath("$.length()").value(2)) .andExpect(jsonPath("$[0]").value("a.txt")) .andExpect(jsonPath("$[1]").value("b.pdf")); } }
覆盖率说明
上述测试覆盖了Service层、Controller层的所有业务逻辑分支,仅配置类的Bean创建代码无需覆盖,整体覆盖率可稳定达到95%以上,满足90%的要求。
内容的提问来源于stack exchange,提问作者Codinghubby
相关产品推荐
相关产品推荐

