单元测试中ResponseEntity返回fullname为null的异常问题排查
问题原因与解决方案:ResponseEntity中@JsonIgnore导致测试断言失败
问题现象
单元测试中,断言返回的EntityResponseDTO的fullname字段为"foobar"时失败,该字段返回null;但在控制器返回ResponseEntity之前,打印内存中的EntityResponseDTO对象,fullname的值却是正确的"foobar"。移除fullname字段上的@JsonIgnore注解后,测试通过。
测试代码
@Test @DirtiesContext public void createEntityTest() { EntityRequestDTO requestDTO = new EntityRequestDTO(); dto.setTitle("foo"); dto.setDescription("bar"); ResponseEntity<EntityResponseDTO> postResponse = testRestTemplate.postForEntity("/api/test", dto, EntityResponseDTO.class); EntityResponseDTO responseDTO = postResponse.getBody(); assertThat(responseDTO.getTitle()).isEqualTo("foo"); assertThat(responseDTO.getDescription()).isEqualTo("bar"); assertThat(responseDTO.getFullname()).isEqualTo("foobar"); }
控制器代码
@PostMapping("/api/test") public ResponseEntity<EntityResponseDTO> create(@RequestBody EntityRequestDTO requestDTO) { Entity saved = entityService.add(requestDTO); EntityResponseDTO savedDto = new EntityResponseDTO(saved); URI location = ServletUriComponentsBuilder .fromCurrentRequest() .path("/{id}") .buildAndExpand(saved.getId()) .toUri(); ResponseEntity<EntityResponseDTO> resEntity = ResponseEntity.created(location).body(savedDto); System.out.println("Before: ResponseEntity in controller."); System.out.println(resEntity.getBody().getFullname()); }
原因分析
@JsonIgnore注解的默认行为是同时控制序列化和反序列化:序列化Java对象为JSON时会忽略该字段,反序列化JSON为Java对象时也会跳过该字段的赋值。- 控制器中打印的是内存中的
EntityResponseDTO实例,还未经过Jackson的序列化处理,所以fullname字段保留了服务层设置的值。 - 当控制器返回
ResponseEntity时,Spring会通过Jackson将EntityResponseDTO序列化为JSON响应体,此时@JsonIgnore生效,fullname字段不会被写入JSON。 TestRestTemplate接收到JSON响应后,会将其反序列化为EntityResponseDTO对象,由于响应体中没有fullname字段,该字段最终为null,导致断言失败。
解决方案
根据你的需求场景,可选择以下方案:
1. 保留字段但序列化时隐藏,反序列化时正常读取
将@JsonIgnore替换为@JsonProperty(access = JsonProperty.Access.WRITE_ONLY),该注解表示字段仅在**写入(序列化)**时被忽略,读取(反序列化)时仍会正常解析:
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private String fullname;
2. 仅测试时验证该字段,生产环境保持隐藏
- 编写测试专用的
EntityResponseDTO类,去掉@JsonIgnore注解,测试时使用该类接收响应:
// 测试专用DTO public class TestEntityResponseDTO extends EntityResponseDTO { @Override public String getFullname() { return super.getFullname(); } } // 测试代码中替换为 ResponseEntity<TestEntityResponseDTO> postResponse = testRestTemplate.postForEntity("/api/test", dto, TestEntityResponseDTO.class);
- 或者直接测试服务层的
entityService.add()方法,验证返回的Entity对象的fullname是否正确,跳过Controller层的HTTP序列化/反序列化环节。
3. 生产环境也需要返回该字段
直接移除fullname字段上的@JsonIgnore注解即可。
内容的提问来源于stack exchange,提问作者IceMajor
相关产品推荐
相关产品推荐

