使用TestRestTemplate集成测试时已传请求体仍报缺失错误
问题
我开发了一个Spring Boot应用,该应用调用两个第三方API并根据结果返回CSV文件。应用的接口接收一个@RequestBody参数,类型为List<String>,用于第三方API调用。
尝试使用TestRestTemplate对该应用进行集成测试时,明明已向HttpEntity传入请求体,却收到HttpMessageNotReadableException: Required request body is missing错误。
将相同请求体传入Postman,或使用MockMvc进行控制器测试时均能正常运行,但我需要进行全链路的自动化端到端测试。
如果传入的内容有问题,我预期会收到JSON解析错误而非请求体缺失错误。我尝试过修改传入参数的类型,以及将Content-Type设置为text/plain,但均未改变或消除该错误。
测试代码如下:
package uk.co.heywood.sonarconverter.integration; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.client.ExpectedCount; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; import uk.co.heywood.sonarconverter.Application; import uk.co.heywood.sonarconverter.utils.TestUtils; @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(properties = {"sonar.qubeToken=aFakeQubeToken", "sonar.cloudToken=aFakeCloudToken"}) class SonarConverterIntegrationTest { private static final String URL = "/api/sonar/diff"; private static final String SONARQUBE_URL = "https://sonarqube.services.mywork.co.uk/api/issues/search?projects=myproject&componentKeys=myComponentKeys&statuses=OPEN&ps=500&p=1"; private static final String SONARCLOUD_URL = "https://sonarcloud.io/api/issues/search?projects=myCloudProject&statuses=OPEN&ps=500&p=1"; private static final String SONARQUBE_RESPONSE = TestUtils.fromFile( "integration/sonarqube-response.json" ); private static final String SONARCLOUD_RESPONSE = TestUtils.fromFile( "integration/sonarcloud-response.json" ); private static final String EXPECTED_RESULT = TestUtils.fromFile( "integration/result.csv" ); @Autowired private TestRestTemplate testRestTemplate; @Autowired private RestTemplate restTemplate; private MockRestServiceServer mockRestServiceServer; @BeforeEach void setUp() { mockRestServiceServer = MockRestServiceServer.createServer( restTemplate ); } @Test void canCreateCsv() { mockRestServiceServer.expect( ExpectedCount.once(), requestTo(SONARQUBE_URL)) .andExpect( method(HttpMethod.GET) ) .andExpect( header( HttpHeaders.AUTHORIZATION, "Bearer aFakeQubeToken" ) ) .andRespond( withStatus( HttpStatus.OK ) .contentType( MediaType.APPLICATION_JSON ) .body( SONARQUBE_RESPONSE )); mockRestServiceServer.expect( ExpectedCount.once(), requestTo(SONARCLOUD_URL)) .andExpect( method(HttpMethod.GET) ) .andExpect( header( HttpHeaders.AUTHORIZATION, "Bearer aFakeCloudToken" ) ) .andRespond( withStatus( HttpStatus.OK ) .contentType( MediaType.APPLICATION_JSON ) .body( SONARCLOUD_RESPONSE )); HttpHeaders headers = new HttpHeaders(); headers.add( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE ); HttpEntity<String> request = new HttpEntity<>( """[ "aParameter", "anotherParameter" ]""", headers ); ResponseEntity<String> response = testRestTemplate.exchange( URL, HttpMethod.GET, request, String.class ); assertThat(response.getStatusCode()).isEqualTo( HttpStatus.OK ); assertThat(response.getBody()).isEqualTo( EXPECTED_RESULT ); } }
解决方案
问题核心是GET请求不支持携带请求体:虽然部分工具(如Postman)允许GET请求带请求体,但这不符合HTTP规范,Spring的TestRestTemplate会直接忽略GET请求中的请求体,导致后端接口提示"请求体缺失"。
解决步骤:
- 修改控制器接口的请求方法:将接口的
@GetMapping改为@PostMapping,传递List<String>这类复杂参数,POST是更合规的选择。 - 修改测试代码的请求方法:把测试中
testRestTemplate.exchange的HttpMethod.GET替换为HttpMethod.POST。 - 优化请求体构造方式:避免手动拼接JSON字符串,用
ObjectMapper序列化List,减少格式错误:// 注入ObjectMapper @Autowired private ObjectMapper objectMapper; // 构造请求体 List<String> params = Arrays.asList("aParameter", "anotherParameter"); String requestBody = objectMapper.writeValueAsString(params); HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
修改后的测试方法核心代码:
@Test void canCreateCsv() { // ... 第三方API mock代码保持不变 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); List<String> params = Arrays.asList("aParameter", "anotherParameter"); String requestBody = objectMapper.writeValueAsString(params); HttpEntity<String> request = new HttpEntity<>(requestBody, headers); ResponseEntity<String> response = testRestTemplate.exchange( URL, HttpMethod.POST, // 改为POST request, String.class ); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(response.getBody()).isEqualTo(EXPECTED_RESULT); }
内容的提问来源于stack exchange,提问作者Sophie Brown
相关产品推荐
相关产品推荐

