Spring Boot测试与生产环境JSON序列化不一致问题排查
Spring Boot生产环境与单元测试JSON序列化/反序列化行为不一致问题
问题背景
Spring Boot默认JSON序列化/反序列化在生产环境与单元测试中的表现不一致:通过./gradlew bootRun启动应用时正常工作的功能,在单元测试中却失败。希望找到原因,无需编写自定义配置模拟生产环境,让两者行为一致。
复现环境与代码
使用Spring Initializr创建基于Spring Boot 3.1.2、Java 17的极简API服务,依赖Lombok、Guava、WebFlux(WebFlux对该问题无影响)。
实体类与控制器代码
import lombok.*; import com.google.common.collect.*; @Builder @Value public class SearchResult { @Singular ImmutableList<String> results; } @RestController @RequestMapping("/api/v1/search") final class SearchController { @GetMapping(produces = "application/json") public Mono<SearchResult> search() { return Mono.just(SearchResult.builder().result("result1").result("result2").build()); } }
单元测试代码
@AutoConfigureWebTestClient @SpringBootTest final class SearchControllerTest { @Autowired private WebTestClient webTestClient; @Test public void search() { var response = webTestClient.get() .uri("/api/v1/search") .header("Content-Type", "application/json") .exchange(); response.expectStatus().isOk(); response.expectBody(SearchResult.class).isEqualTo(...); } }
测试失败现象
测试失败存在两个核心问题:
- 使用
ImmutableList时出现模糊错误,替换为java.util.List后错误消失; - 提示无法反序列化无默认构造方法的对象。
已尝试的临时解决方案
可以通过以下方式临时解决,但不符合需求:
- 移除
@Value注解,改用Javarecord(需保证不可变性); - 在单元测试中自定义
ObjectMapper并注册GuavaModule:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.guava.GuavaModule; @Configuration public class TestConfig { @Bean public ObjectMapper objectMapper() { var mapper = new ObjectMapper(); mapper.registerModule(new GuavaModule()); return mapper; } }
期望的解决方案是让测试直接使用生产代码中已注入的ObjectMapper,保证测试真实性,同时复用生产环境已支持的特性,同时想了解测试与生产环境配置不同的原因。
问题更新
更新1:生产环境反序列化同样存在问题
仅序列化行为符合预期,生产环境中的反序列化行为与单元测试同样失败。通过在SearchController中新增端点验证:
@GetMapping("/test") public Mono<SearchResult> test() { return webClient.get() .uri("http://localhost:8080/api/v1/search") .exchangeToMono(response -> response.statusCode().equals(HttpStatus.OK) ? response.bodyToMono(SearchResult.class) : response.createError()) .flatMap(response -> Mono.just(response.toBuilder().result("result3").build())); }
已知GuavaModule可解决Guava相关序列化问题,但希望在不添加显式默认构造方法的前提下,继续使用lombok.Value注解。
更新2:@Value配合Jackson反序列化的解决方案
关于@Value注解配合Jackson反序列化的问题已解决:通过在@Builder注解上添加@Jacksonized,即可让Jackson识别Lombok生成的构建器,无需为SearchResult添加默认构造方法,同时保留@Value带来的不可变性。
内容的提问来源于stack exchange,提问作者Baxter Freely
相关产品推荐
相关产品推荐

