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

使用RestTemplate的文件下载接口单元测试出现404错误求助

问题分析与解决方案

首先得明确:你遇到的404不是Controller接口本身返回的,而是Controller内部用RestTemplate调用外部接口时,测试环境下目标地址不存在导致的,核心原因有几个:

核心问题根源

  1. RestTemplate未依赖注入,Mock完全无效
    你的Controller代码里直接在方法内new RestTemplate()创建实例,而不是通过构造函数或@Autowired注入。这就导致测试中@Mock的mockRestTemplate根本不会被用到,测试时依然会发起真实的HTTP请求到PSASConfig.PREFIX_URL_API_PSAS,而测试环境下这个地址大概率不存在,自然返回404。

  2. Mock方法的参数类型不匹配
    就算RestTemplate被正确注入,你测试里mockexchange方法时,最后一个参数写了ArgumentMatchers.<Class<String>>any(),但原代码中实际传入的是Resource.class,类型不匹配会让Mock规则不生效,同样会触发真实请求。

  3. Mock返回的ResponseEntity不完整
    当前Mock返回的ResponseEntity<Resource>没有携带有效的Resource实例,后续调用result.getBody().getInputStream()会直接抛出NullPointerException。


分步解决方案

第一步:重构Controller,让RestTemplate可注入

修改Controller,通过构造函数注入RestTemplate,替代方法内的硬编码实例化:

@Api(value = "Telecharger fichier sas")
@RestController
@RequestMapping(PUBLIC_BASE_URL)
public class TelechargerFichierRestController extends BaseController {
    private static final int BUFFER_SIZE = 4096;
    private static final String HEADER_NAME = "REMOTE_USER";
    
    // 注入RestTemplate实例
    private final RestTemplate restTemplate;

    public TelechargerFichierRestController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @ApiOperation(value = "Telecharger fichier dans le répertoire download")
    @RequestMapping(value = "/sasDl/download", method = RequestMethod.GET)
    public void downloadFichier(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
        InputStream inputStream = null;
        int bytesRead;
        // 不再手动new,使用注入的实例
        HttpHeaders headers = new HttpHeaders();
        String remoteUser = request.getHeader(HEADER_NAME);
        headers.add(HEADER_NAME, remoteUser);
        HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
        ResponseEntity<Resource> result = restTemplate.exchange(PSASConfig.PREFIX_URL_API_PSAS, HttpMethod.GET, entity, Resource.class);
        
        OutputStream outStream = response.getOutputStream();
        try {
            inputStream = result.getBody().getInputStream();
        } catch (IOException e) {
            throw new IOException(e);
        }
        byte[] buffer = new byte[BUFFER_SIZE];
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }
        inputStream.close();
        outStream.close();
    }
}

如果你的项目还没配置RestTemplate的Bean,需要在配置类中添加:

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

第二步:修正测试代码的Mock逻辑

修改测试代码,确保Mock的参数类型匹配,并且返回有效的Resource实例:

@RunWith(MockitoJUnitRunner.class)
public class TelechargerFichierRestTest {
    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;
    @Mock
    private RestTemplate mockRestTemplate;
    @Mock
    private Resource mockResource;
    @Mock
    private InputStream mockInputStream;

    @InjectMocks
    private TelechargerFichierRestController restController;

    @Test
    public void testDownloadFichier() throws Exception {
        // 模拟请求头
        Mockito.when(request.getHeader("REMOTE_USER")).thenReturn("test-user");
        
        // 模拟文件流读取行为(直接返回-1表示读取完毕)
        Mockito.when(mockInputStream.read(Mockito.any(byte[].class))).thenReturn(-1);
        Mockito.when(mockResource.getInputStream()).thenReturn(mockInputStream);
        
        // 正确匹配exchange方法的参数类型
        Mockito.when(mockRestTemplate.exchange(
                ArgumentMatchers.eq(PSASConfig.PREFIX_URL_API_PSAS),
                ArgumentMatchers.eq(HttpMethod.GET),
                ArgumentMatchers.any(HttpEntity.class),
                ArgumentMatchers.eq(Resource.class)
        )).thenReturn(new ResponseEntity<>(mockResource, HttpStatus.OK));
        
        // 模拟响应输出流
        OutputStream mockOutputStream = Mockito.mock(OutputStream.class);
        Mockito.when(response.getOutputStream()).thenReturn(mockOutputStream);
        
        // 执行测试方法
        restController.downloadFichier(request, response);
        
        // 验证关键交互
        Mockito.verify(request).getHeader("REMOTE_USER");
        Mockito.verify(response).getOutputStream();
        Mockito.verify(mockInputStream).read(Mockito.any(byte[].class));
        Mockito.verify(mockOutputStream).write(Mockito.any(byte[].class), Mockito.eq(0), Mockito.eq(-1));
        Mockito.verify(mockInputStream).close();
        Mockito.verify(mockOutputStream).close();
    }
}

这样修改后,测试会完全Mock掉外部接口的调用,不会再发起真实请求,也就不会出现404错误了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:20:16