You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MockMvc集成测试:模拟Azure存储组件时请求挂起或序列化异常

问题分析与解决方案:文件下载接口MockMvc测试中的InputStreamResource模拟问题

问题现象

  • 调用文件下载接口的MockMvc测试时,请求无限挂起;切换到WebTestClient后同样出现超时,问题不局限于测试工具
  • 调整配置后测试不再挂起,但抛出HttpMessageNotWritableException
  • 已定位问题根源为模拟InputStreamResource的方式不正确

最小复现场景代码

1. 控制器代码

@RestController
@RequestMapping("/download")
public class DownloadController {

    @GetMapping
    public ResponseEntity<InputStreamResource> downloadFile() throws IOException {
        File file = new File("test.txt");
        FileInputStream fis = new FileInputStream(file);
        InputStreamResource resource = new InputStreamResource(fis);

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"test.txt\"")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }
}

2. 测试类代码(错误示例)

@SpringBootTest
@AutoConfigureMockMvc
public class DownloadControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testDownload() throws Exception {
        // 错误的InputStreamResource模拟方式
        InputStreamResource mockResource = new InputStreamResource(new ByteArrayInputStream("test content".getBytes()));

        mockMvc.perform(get("/download")
                        .contentType(MediaType.APPLICATION_OCTET_STREAM))
                .andExpect(status().isOk())
                .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"test.txt\""))
                .andExpect(content().bytes("test content".getBytes()));
    }
}

3. POM核心依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

问题根源

  1. 无限挂起/超时原因:Spring消息转换器处理InputStreamResource时,会尝试读取流直到结束。如果模拟的流(如ByteArrayInputStream)在测试场景中未被正确消费,或测试工具的流读取逻辑存在阻塞,会导致框架持续等待流数据,最终超时或挂起。
  2. HttpMessageNotWritableException原因:调整配置后,若模拟的InputStreamResource关联的流已关闭、不可读,或消息转换器未正确适配InputStreamResource类型,会导致转换器无法写入响应体,抛出该异常。

正确解决方案

方案1:用ByteArrayResource替代模拟InputStreamResource

ByteArrayResource是Spring提供的更适合测试的资源类型,无需处理流生命周期问题:

@Test
void testDownload() throws Exception {
    byte[] content = "test content".getBytes();

    mockMvc.perform(get("/download"))
            .andExpect(status().isOk())
            .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("filename=\"test.txt\"")))
            .andExpect(content().bytes(content));
}

如果控制器依赖外部文件服务,可通过@MockBean注入模拟服务并返回ByteArrayResource。

方案2:正确模拟InputStreamResource并确保流可被消费

若必须使用InputStreamResource,需保证流可读且未关闭,同时在测试中主动消费响应体:

@Test
void testDownloadWithInputStreamResource() throws Exception {
    mockMvc.perform(get("/download"))
            .andExpect(status().isOk())
            .andDo(result -> {
                // 手动读取响应体,避免框架阻塞
                byte[] responseBytes = result.getResponse().getContentAsByteArray();
                assertArrayEquals("test content".getBytes(), responseBytes);
            });
}

方案3:调整消息转换器配置(复杂场景)

如果项目自定义了消息转换器,需确保ResourceHttpMessageConverter正确配置并支持InputStreamResource:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        ResourceHttpMessageConverter resourceConverter = new ResourceHttpMessageConverter();
        resourceConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
        converters.add(resourceConverter);
    }
}

关键注意事项

  • 测试中优先使用ByteArrayResource或FileSystemResource,避免直接模拟InputStreamResource
  • 确保测试中的输入流未被提前关闭,且具备可读能力
  • 使用MockMvc时,通过andDo()手动消费响应体可避免流读取阻塞问题

内容的提问来源于stack exchange,提问作者solujic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:45:57